Implements support for calling Test::RecordProperty() outside of a test.
This commit is contained in:
		| @@ -158,6 +158,7 @@ class StreamingListenerTest; | ||||
| class TestResultAccessor; | ||||
| class TestEventListenersAccessor; | ||||
| class TestEventRepeater; | ||||
| class UnitTestRecordPropertyTestHelper; | ||||
| class WindowsDeathTest; | ||||
| class UnitTestImpl* GetUnitTestImpl(); | ||||
| void ReportFailureInUnknownLocation(TestPartResult::Type result_type, | ||||
| @@ -381,20 +382,21 @@ class GTEST_API_ Test { | ||||
|   // non-fatal) failure. | ||||
|   static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } | ||||
|  | ||||
|   // Logs a property for the current test.  Only the last value for a given | ||||
|   // key is remembered. | ||||
|   // These are public static so they can be called from utility functions | ||||
|   // that are not members of the test fixture. | ||||
|   // The arguments are const char* instead strings, as Google Test is used | ||||
|   // on platforms where string doesn't compile. | ||||
|   // | ||||
|   // Note that a driving consideration for these RecordProperty methods | ||||
|   // was to produce xml output suited to the Greenspan charting utility, | ||||
|   // which at present will only chart values that fit in a 32-bit int. It | ||||
|   // is the user's responsibility to restrict their values to 32-bit ints | ||||
|   // if they intend them to be used with Greenspan. | ||||
|   static void RecordProperty(const char* key, const char* value); | ||||
|   static void RecordProperty(const char* key, int value); | ||||
|   // Logs a property for the current test, test case, or for the entire | ||||
|   // invocation of the test program when used outside of the context of a | ||||
|   // test case.  Only the last value for a given key is remembered.  These | ||||
|   // are public static so they can be called from utility functions that are | ||||
|   // not members of the test fixture.  Calls to RecordProperty made during | ||||
|   // lifespan of the test (from the moment its constructor starts to the | ||||
|   // moment its destructor finishes) will be output in XML as attributes of | ||||
|   // the <testcase> element.  Properties recorded from fixture's | ||||
|   // SetUpTestCase or TearDownTestCase are logged as attributes of the | ||||
|   // corresponding <testsuite> element.  Calls to RecordProperty made in the | ||||
|   // global context (before or after invocation of RUN_ALL_TESTS and from | ||||
|   // SetUp/TearDown method of Environment objects registered with Google | ||||
|   // Test) will be output as attributes of the <testsuites> element. | ||||
|   static void RecordProperty(const std::string& key, const std::string& value); | ||||
|   static void RecordProperty(const std::string& key, int value); | ||||
|  | ||||
|  protected: | ||||
|   // Creates a Test object. | ||||
| @@ -463,7 +465,7 @@ class TestProperty { | ||||
|   // C'tor.  TestProperty does NOT have a default constructor. | ||||
|   // Always use this constructor (with parameters) to create a | ||||
|   // 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) { | ||||
|   } | ||||
|  | ||||
| @@ -478,7 +480,7 @@ class TestProperty { | ||||
|   } | ||||
|  | ||||
|   // 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; | ||||
|   } | ||||
|  | ||||
| @@ -537,6 +539,7 @@ class GTEST_API_ TestResult { | ||||
|  | ||||
|  private: | ||||
|   friend class TestInfo; | ||||
|   friend class TestCase; | ||||
|   friend class UnitTest; | ||||
|   friend class internal::DefaultGlobalTestPartResultReporter; | ||||
|   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 | ||||
|   // 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 | ||||
|   // key. | ||||
|   void RecordProperty(const TestProperty& test_property); | ||||
|   // key.  xml_element specifies the element for which the property is being | ||||
|   // 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 | ||||
|   // testcase tags.  Returns true if the property is valid. | ||||
|   // 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. | ||||
|   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. | ||||
|   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: | ||||
|   friend class Test; | ||||
|   friend class internal::UnitTestImpl; | ||||
| @@ -880,6 +890,9 @@ class GTEST_API_ TestCase { | ||||
|   bool should_run_; | ||||
|   // Elapsed time, in milliseconds. | ||||
|   TimeInMillis elapsed_time_; | ||||
|   // Holds test properties recorded during execution of SetUpTestCase and | ||||
|   // TearDownTestCase. | ||||
|   TestResult ad_hoc_test_result_; | ||||
|  | ||||
|   // We disallow copying TestCases. | ||||
|   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. | ||||
|   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 | ||||
|   // inside Google Test. | ||||
|   TestEventListeners& listeners(); | ||||
| @@ -1192,9 +1209,12 @@ class GTEST_API_ UnitTest { | ||||
|                          const std::string& os_stack_trace) | ||||
|       GTEST_LOCK_EXCLUDED_(mutex_); | ||||
|  | ||||
|   // Adds a TestProperty to the current TestResult object. If the result already | ||||
|   // contains a property with the same key, the value will be updated. | ||||
|   void RecordPropertyForCurrentTest(const char* key, const char* value); | ||||
|   // Adds a TestProperty to the current TestResult object when invoked from | ||||
|   // inside a test, to current TestCase's ad_hoc_test_result_ when invoked | ||||
|   // 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 | ||||
|   // 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::ScopedTrace; | ||||
|   friend class internal::StreamingListenerTest; | ||||
|   friend class internal::UnitTestRecordPropertyTestHelper; | ||||
|   friend Environment* AddGlobalTestEnvironment(Environment* env); | ||||
|   friend internal::UnitTestImpl* internal::GetUnitTestImpl(); | ||||
|   friend void internal::ReportFailureInUnknownLocation( | ||||
|   | ||||
| @@ -348,8 +348,7 @@ class TestPropertyKeyIs { | ||||
|   // Constructor. | ||||
|   // | ||||
|   // TestPropertyKeyIs has NO default constructor. | ||||
|   explicit TestPropertyKeyIs(const char* key) | ||||
|       : key_(key) {} | ||||
|   explicit TestPropertyKeyIs(const std::string& key) : key_(key) {} | ||||
|  | ||||
|   // Returns true iff the test name of test property matches on key_. | ||||
|   bool operator()(const TestProperty& test_property) const { | ||||
| @@ -712,6 +711,12 @@ class GTEST_API_ UnitTestImpl { | ||||
|     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 { | ||||
|     HONOR_SHARDING_PROTOCOL, | ||||
|     IGNORE_SHARDING_PROTOCOL | ||||
| @@ -1038,8 +1043,9 @@ bool ParseNaturalNumber(const ::std::string& str, Integer* number) { | ||||
| class TestResultAccessor { | ||||
|  public: | ||||
|   static void RecordProperty(TestResult* test_result, | ||||
|                              const std::string& xml_element, | ||||
|                              const TestProperty& property) { | ||||
|     test_result->RecordProperty(property); | ||||
|     test_result->RecordProperty(xml_element, property); | ||||
|   } | ||||
|  | ||||
|   static void ClearTestPartResults(TestResult* test_result) { | ||||
|   | ||||
							
								
								
									
										370
									
								
								src/gtest.cc
									
									
									
									
									
								
							
							
						
						
									
										370
									
								
								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 | ||||
| // supplied property is already represented, the value of this test_property | ||||
| // replaces the old value for that key. | ||||
| void TestResult::RecordProperty(const TestProperty& test_property) { | ||||
|   if (!ValidateTestProperty(test_property)) { | ||||
| void TestResult::RecordProperty(const std::string& xml_element, | ||||
|                                 const TestProperty& test_property) { | ||||
|   if (!ValidateTestProperty(xml_element, test_property)) { | ||||
|     return; | ||||
|   } | ||||
|   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()); | ||||
| } | ||||
|  | ||||
| // Adds a failure if the key is a reserved attribute of Google Test | ||||
| // testcase tags.  Returns true if the property is valid. | ||||
| bool TestResult::ValidateTestProperty(const TestProperty& test_property) { | ||||
|   const std::string& key = test_property.key(); | ||||
|   if (key == "name" || key == "status" || key == "time" || key == "classname") { | ||||
|     ADD_FAILURE() | ||||
|         << "Reserved key used in RecordProperty(): " | ||||
|         << key | ||||
|         << " ('name', 'status', 'time', and 'classname' are reserved by " | ||||
|         << GTEST_NAME_ << ")"; | ||||
| // The list of reserved attributes used in the <testsuites> element of XML | ||||
| // output. | ||||
| static const char* const kReservedTestSuitesAttributes[] = { | ||||
|   "disabled", | ||||
|   "errors", | ||||
|   "failures", | ||||
|   "name", | ||||
|   "random_seed", | ||||
|   "tests", | ||||
|   "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 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. | ||||
| void TestResult::Clear() { | ||||
|   test_part_results_.clear(); | ||||
| @@ -1821,12 +1895,12 @@ void Test::TearDown() { | ||||
| } | ||||
|  | ||||
| // Allows user supplied key value pairs to be recorded for later output. | ||||
| void Test::RecordProperty(const char* key, const char* value) { | ||||
|   UnitTest::GetInstance()->RecordPropertyForCurrentTest(key, value); | ||||
| void Test::RecordProperty(const std::string& key, const std::string& value) { | ||||
|   UnitTest::GetInstance()->RecordProperty(key, value); | ||||
| } | ||||
|  | ||||
| // 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; | ||||
|   value_message << value; | ||||
|   RecordProperty(key, value_message.GetString().c_str()); | ||||
| @@ -2355,6 +2429,7 @@ void TestCase::Run() { | ||||
|  | ||||
| // Clears the results of all tests in this test case. | ||||
| void TestCase::ClearResult() { | ||||
|   ad_hoc_test_result_.Clear(); | ||||
|   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 | ||||
|   // value, and normalizable whitespace is preserved by replacing it | ||||
|   // 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. | ||||
|   static string RemoveInvalidXmlCharacters(const string& str); | ||||
|   static std::string RemoveInvalidXmlCharacters(const std::string& str); | ||||
|  | ||||
|   // 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); | ||||
|   } | ||||
|  | ||||
| @@ -2940,6 +3015,13 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener { | ||||
|     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. | ||||
|   static void OutputXmlCDataSection(::std::ostream* stream, const char* data); | ||||
|  | ||||
| @@ -2949,10 +3031,12 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener { | ||||
|                                 const TestInfo& test_info); | ||||
|  | ||||
|   // 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. | ||||
|   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 | ||||
|   // delimited XML attributes based on the property key="value" pairs. | ||||
| @@ -3003,7 +3087,9 @@ void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, | ||||
|     fflush(stderr); | ||||
|     exit(EXIT_FAILURE); | ||||
|   } | ||||
|   PrintXmlUnitTest(xmlout, unit_test); | ||||
|   std::stringstream stream; | ||||
|   PrintXmlUnitTest(&stream, unit_test); | ||||
|   fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); | ||||
|   fclose(xmlout); | ||||
| } | ||||
|  | ||||
| @@ -3020,43 +3106,42 @@ void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, | ||||
| // TODO(wan): It might be nice to have a minimally invasive, human-readable | ||||
| // escaping scheme for invalid characters, rather than dropping them. | ||||
| std::string XmlUnitTestResultPrinter::EscapeXml( | ||||
|     const char* str, bool is_attribute) { | ||||
|     const std::string& str, bool is_attribute) { | ||||
|   Message m; | ||||
|  | ||||
|   if (str != NULL) { | ||||
|     for (const char* src = str; *src; ++src) { | ||||
|       switch (*src) { | ||||
|         case '<': | ||||
|           m << "<"; | ||||
|           break; | ||||
|         case '>': | ||||
|           m << ">"; | ||||
|           break; | ||||
|         case '&': | ||||
|           m << "&"; | ||||
|           break; | ||||
|         case '\'': | ||||
|           if (is_attribute) | ||||
|             m << "'"; | ||||
|   for (size_t i = 0; i < str.size(); ++i) { | ||||
|     const char ch = str[i]; | ||||
|     switch (ch) { | ||||
|       case '<': | ||||
|         m << "<"; | ||||
|         break; | ||||
|       case '>': | ||||
|         m << ">"; | ||||
|         break; | ||||
|       case '&': | ||||
|         m << "&"; | ||||
|         break; | ||||
|       case '\'': | ||||
|         if (is_attribute) | ||||
|           m << "'"; | ||||
|         else | ||||
|           m << '\''; | ||||
|         break; | ||||
|       case '"': | ||||
|         if (is_attribute) | ||||
|           m << """; | ||||
|         else | ||||
|           m << '"'; | ||||
|         break; | ||||
|       default: | ||||
|         if (IsValidXmlCharacter(ch)) { | ||||
|           if (is_attribute && IsNormalizableWhitespace(ch)) | ||||
|             m << "&#x" << String::FormatByte(static_cast<unsigned char>(ch)) | ||||
|               << ";"; | ||||
|           else | ||||
|             m << '\''; | ||||
|           break; | ||||
|         case '"': | ||||
|           if (is_attribute) | ||||
|             m << """; | ||||
|           else | ||||
|             m << '"'; | ||||
|           break; | ||||
|         default: | ||||
|           if (IsValidXmlCharacter(*src)) { | ||||
|             if (is_attribute && IsNormalizableWhitespace(*src)) | ||||
|               m << "&#x" << String::FormatByte(static_cast<unsigned char>(*src)) | ||||
|                 << ";"; | ||||
|             else | ||||
|               m << *src; | ||||
|           } | ||||
|           break; | ||||
|       } | ||||
|             m << ch; | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -3066,10 +3151,11 @@ std::string XmlUnitTestResultPrinter::EscapeXml( | ||||
| // Returns the given string with all characters invalid in XML removed. | ||||
| // Currently invalid characters are dropped from the string. An | ||||
| // alternative is to replace them with certain characters such as . or ?. | ||||
| string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(const string& str) { | ||||
|   string output; | ||||
| std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters( | ||||
|     const std::string& str) { | ||||
|   std::string output; | ||||
|   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)) | ||||
|       output.push_back(*it); | ||||
|  | ||||
| @@ -3145,30 +3231,47 @@ void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* 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. | ||||
| // TODO(wan): There is also value in printing properties with the plain printer. | ||||
| void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, | ||||
|                                                  const char* test_case_name, | ||||
|                                                  const TestInfo& test_info) { | ||||
|   const TestResult& result = *test_info.result(); | ||||
|   *stream << "    <testcase name=\"" | ||||
|           << EscapeXmlAttribute(test_info.name()).c_str() << "\""; | ||||
|   const std::string kTestcase = "testcase"; | ||||
|  | ||||
|   *stream << "    <testcase"; | ||||
|   OutputXmlAttribute(stream, kTestcase, "name", test_info.name()); | ||||
|  | ||||
|   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) { | ||||
|     *stream << " type_param=\"" << EscapeXmlAttribute(test_info.type_param()) | ||||
|             << "\""; | ||||
|     OutputXmlAttribute(stream, kTestcase, "type_param", test_info.type_param()); | ||||
|   } | ||||
|  | ||||
|   *stream << " status=\"" | ||||
|           << (test_info.should_run() ? "run" : "notrun") | ||||
|           << "\" time=\"" | ||||
|           << FormatTimeInMillisAsSeconds(result.elapsed_time()) | ||||
|           << "\" classname=\"" << EscapeXmlAttribute(test_case_name).c_str() | ||||
|           << "\"" << TestPropertiesAsXmlAttributes(result).c_str(); | ||||
|   OutputXmlAttribute(stream, kTestcase, "status", | ||||
|                      test_info.should_run() ? "run" : "notrun"); | ||||
|   OutputXmlAttribute(stream, kTestcase, "time", | ||||
|                      FormatTimeInMillisAsSeconds(result.elapsed_time())); | ||||
|   OutputXmlAttribute(stream, kTestcase, "classname", test_case_name); | ||||
|   *stream << TestPropertiesAsXmlAttributes(result); | ||||
|  | ||||
|   int failures = 0; | ||||
|   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 | ||||
| void XmlUnitTestResultPrinter::PrintXmlTestCase(FILE* out, | ||||
| void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream, | ||||
|                                                 const TestCase& test_case) { | ||||
|   fprintf(out, | ||||
|           "  <testsuite name=\"%s\" tests=\"%d\" failures=\"%d\" " | ||||
|           "disabled=\"%d\" ", | ||||
|           EscapeXmlAttribute(test_case.name()).c_str(), | ||||
|           test_case.total_test_count(), | ||||
|           test_case.failed_test_count(), | ||||
|           test_case.disabled_test_count()); | ||||
|   fprintf(out, | ||||
|           "errors=\"0\" time=\"%s\">\n", | ||||
|           FormatTimeInMillisAsSeconds(test_case.elapsed_time()).c_str()); | ||||
|   for (int i = 0; i < test_case.total_test_count(); ++i) { | ||||
|     ::std::stringstream stream; | ||||
|     OutputXmlTestInfo(&stream, test_case.name(), *test_case.GetTestInfo(i)); | ||||
|     fprintf(out, "%s", StringStreamToString(&stream).c_str()); | ||||
|   } | ||||
|   fprintf(out, "  </testsuite>\n"); | ||||
|   const std::string kTestsuite = "testsuite"; | ||||
|   *stream << "  <" << kTestsuite; | ||||
|   OutputXmlAttribute(stream, kTestsuite, "name", test_case.name()); | ||||
|   OutputXmlAttribute(stream, kTestsuite, "tests", | ||||
|                      StreamableToString(test_case.total_test_count())); | ||||
|   OutputXmlAttribute(stream, kTestsuite, "failures", | ||||
|                      StreamableToString(test_case.failed_test_count())); | ||||
|   OutputXmlAttribute(stream, kTestsuite, "disabled", | ||||
|                      StreamableToString(test_case.disabled_test_count())); | ||||
|   OutputXmlAttribute(stream, kTestsuite, "errors", "0"); | ||||
|   OutputXmlAttribute(stream, kTestsuite, "time", | ||||
|                      FormatTimeInMillisAsSeconds(test_case.elapsed_time())); | ||||
|   *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result()) | ||||
|           << ">\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. | ||||
| void XmlUnitTestResultPrinter::PrintXmlUnitTest(FILE* out, | ||||
| void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, | ||||
|                                                 const UnitTest& unit_test) { | ||||
|   fprintf(out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | ||||
|   fprintf(out, | ||||
|           "<testsuites tests=\"%d\" failures=\"%d\" disabled=\"%d\" " | ||||
|           "errors=\"0\" timestamp=\"%s\" time=\"%s\" ", | ||||
|           unit_test.total_test_count(), | ||||
|           unit_test.failed_test_count(), | ||||
|           unit_test.disabled_test_count(), | ||||
|           FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp()).c_str(), | ||||
|           FormatTimeInMillisAsSeconds(unit_test.elapsed_time()).c_str()); | ||||
|   const std::string kTestsuites = "testsuites"; | ||||
|  | ||||
|   *stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; | ||||
|   *stream << "<" << kTestsuites; | ||||
|  | ||||
|   OutputXmlAttribute(stream, kTestsuites, "tests", | ||||
|                      StreamableToString(unit_test.total_test_count())); | ||||
|   OutputXmlAttribute(stream, kTestsuites, "failures", | ||||
|                      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)) { | ||||
|     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) | ||||
|     PrintXmlTestCase(out, *unit_test.GetTestCase(i)); | ||||
|   fprintf(out, "</testsuites>\n"); | ||||
|  | ||||
|   *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); | ||||
|  | ||||
|   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 | ||||
| @@ -3452,7 +3574,7 @@ void TestEventListeners::SuppressEventForwarding() { | ||||
| // 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 | ||||
| // value will never change. | ||||
| UnitTest * UnitTest::GetInstance() { | ||||
| UnitTest* UnitTest::GetInstance() { | ||||
|   // When compiled with MSVC 7.1 in optimized mode, destroying the | ||||
|   // UnitTest object upon exiting the program messes up the exit code, | ||||
|   // 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); | ||||
| } | ||||
|  | ||||
| // 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 | ||||
| // total_test_case_count() - 1. If i is not in that range, returns NULL. | ||||
| 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 | ||||
| // the supplied value already exists, updates its value instead. | ||||
| void UnitTest::RecordPropertyForCurrentTest(const char* key, | ||||
|                                             const char* value) { | ||||
|   const TestProperty test_property(key, value); | ||||
|   impl_->current_test_result()->RecordProperty(test_property); | ||||
| // Adds a TestProperty to the current TestResult object when invoked from | ||||
| // inside a test, to current TestCase's ad_hoc_test_result_ when invoked | ||||
| // 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 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. | ||||
| @@ -3813,6 +3943,28 @@ UnitTestImpl::~UnitTestImpl() { | ||||
|   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 | ||||
| // Disables event forwarding if the control is currently in a death test | ||||
| // 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 testing | ||||
|  | ||||
| @@ -188,6 +200,7 @@ using testing::AssertionResult; | ||||
| using testing::AssertionSuccess; | ||||
| using testing::DoubleLE; | ||||
| using testing::EmptyTestEventListener; | ||||
| using testing::Environment; | ||||
| using testing::FloatLE; | ||||
| using testing::GTEST_FLAG(also_run_disabled_tests); | ||||
| using testing::GTEST_FLAG(break_on_failure); | ||||
| @@ -213,6 +226,7 @@ using testing::StaticAssertTypeEq; | ||||
| using testing::Test; | ||||
| using testing::TestCase; | ||||
| using testing::TestEventListeners; | ||||
| using testing::TestInfo; | ||||
| using testing::TestPartResult; | ||||
| using testing::TestPartResultArray; | ||||
| using testing::TestProperty; | ||||
| @@ -1447,7 +1461,7 @@ TEST(TestResultPropertyTest, NoPropertiesFoundWhenNoneAreAdded) { | ||||
| TEST(TestResultPropertyTest, OnePropertyFoundWhenAdded) { | ||||
|   TestResult test_result; | ||||
|   TestProperty property("key_1", "1"); | ||||
|   TestResultAccessor::RecordProperty(&test_result, property); | ||||
|   TestResultAccessor::RecordProperty(&test_result, "testcase", property); | ||||
|   ASSERT_EQ(1, test_result.test_property_count()); | ||||
|   const TestProperty& actual_property = test_result.GetTestProperty(0); | ||||
|   EXPECT_STREQ("key_1", actual_property.key()); | ||||
| @@ -1459,8 +1473,8 @@ TEST(TestResultPropertyTest, MultiplePropertiesFoundWhenAdded) { | ||||
|   TestResult test_result; | ||||
|   TestProperty property_1("key_1", "1"); | ||||
|   TestProperty property_2("key_2", "2"); | ||||
|   TestResultAccessor::RecordProperty(&test_result, property_1); | ||||
|   TestResultAccessor::RecordProperty(&test_result, property_2); | ||||
|   TestResultAccessor::RecordProperty(&test_result, "testcase", property_1); | ||||
|   TestResultAccessor::RecordProperty(&test_result, "testcase", property_2); | ||||
|   ASSERT_EQ(2, test_result.test_property_count()); | ||||
|   const TestProperty& actual_property_1 = test_result.GetTestProperty(0); | ||||
|   EXPECT_STREQ("key_1", actual_property_1.key()); | ||||
| @@ -1478,10 +1492,10 @@ TEST(TestResultPropertyTest, OverridesValuesForDuplicateKeys) { | ||||
|   TestProperty property_2_1("key_2", "2"); | ||||
|   TestProperty property_1_2("key_1", "12"); | ||||
|   TestProperty property_2_2("key_2", "22"); | ||||
|   TestResultAccessor::RecordProperty(&test_result, property_1_1); | ||||
|   TestResultAccessor::RecordProperty(&test_result, property_2_1); | ||||
|   TestResultAccessor::RecordProperty(&test_result, property_1_2); | ||||
|   TestResultAccessor::RecordProperty(&test_result, property_2_2); | ||||
|   TestResultAccessor::RecordProperty(&test_result, "testcase", property_1_1); | ||||
|   TestResultAccessor::RecordProperty(&test_result, "testcase", property_2_1); | ||||
|   TestResultAccessor::RecordProperty(&test_result, "testcase", property_1_2); | ||||
|   TestResultAccessor::RecordProperty(&test_result, "testcase", property_2_2); | ||||
|  | ||||
|   ASSERT_EQ(2, test_result.test_property_count()); | ||||
|   const TestProperty& actual_property_1 = test_result.GetTestProperty(0); | ||||
| @@ -1494,14 +1508,14 @@ TEST(TestResultPropertyTest, OverridesValuesForDuplicateKeys) { | ||||
| } | ||||
|  | ||||
| // Tests TestResult::GetTestProperty(). | ||||
| TEST(TestResultPropertyDeathTest, GetTestProperty) { | ||||
| TEST(TestResultPropertyTest, GetTestProperty) { | ||||
|   TestResult test_result; | ||||
|   TestProperty property_1("key_1", "1"); | ||||
|   TestProperty property_2("key_2", "2"); | ||||
|   TestProperty property_3("key_3", "3"); | ||||
|   TestResultAccessor::RecordProperty(&test_result, property_1); | ||||
|   TestResultAccessor::RecordProperty(&test_result, property_2); | ||||
|   TestResultAccessor::RecordProperty(&test_result, property_3); | ||||
|   TestResultAccessor::RecordProperty(&test_result, "testcase", property_1); | ||||
|   TestResultAccessor::RecordProperty(&test_result, "testcase", property_2); | ||||
|   TestResultAccessor::RecordProperty(&test_result, "testcase", property_3); | ||||
|  | ||||
|   const TestProperty& fetched_property_1 = test_result.GetTestProperty(0); | ||||
|   const TestProperty& fetched_property_2 = test_result.GetTestProperty(1); | ||||
| @@ -1520,42 +1534,6 @@ TEST(TestResultPropertyDeathTest, GetTestProperty) { | ||||
|   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. | ||||
|  | ||||
| class GTestFlagSaverTest : public Test { | ||||
| @@ -1961,6 +1939,168 @@ TEST(UnitTestTest, ReturnsPlausibleTimestamp) { | ||||
|   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) | ||||
| // of various arities.  They do not attempt to be exhaustive.  Rather, | ||||
| // view them as smoke tests that can be easily reviewed and verified. | ||||
|   | ||||
| @@ -57,7 +57,7 @@ else: | ||||
|   STACK_TRACE_TEMPLATE = '' | ||||
|  | ||||
| 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="*"> | ||||
|     <testcase name="Succeeds" status="run" time="*" classname="SuccessfulTest"/> | ||||
|   </testsuite> | ||||
| @@ -97,7 +97,7 @@ Invalid characters in brackets []%(stack)s]]></failure> | ||||
|   <testsuite name="DisabledTest" tests="1" failures="0" disabled="1" errors="0" time="*"> | ||||
|     <testcase name="DISABLED_test_not_run" status="notrun" time="*" classname="DisabledTest"/> | ||||
|   </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="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"/> | ||||
|   | ||||
| @@ -95,6 +95,9 @@ TEST(InvalidCharactersTest, InvalidCharactersInMessage) { | ||||
| } | ||||
|  | ||||
| class PropertyRecordingTest : public Test { | ||||
|  public: | ||||
|   static void SetUpTestCase() { RecordProperty("SetUpTestCase", "yes"); } | ||||
|   static void TearDownTestCase() { RecordProperty("TearDownTestCase", "aye"); } | ||||
| }; | ||||
|  | ||||
| TEST_F(PropertyRecordingTest, OneProperty) { | ||||
| @@ -120,12 +123,12 @@ TEST(NoFixtureTest, RecordProperty) { | ||||
|   RecordProperty("key", "1"); | ||||
| } | ||||
|  | ||||
| void ExternalUtilityThatCallsRecordProperty(const char* key, int value) { | ||||
| void ExternalUtilityThatCallsRecordProperty(const std::string& key, int value) { | ||||
|   testing::Test::RecordProperty(key, value); | ||||
| } | ||||
|  | ||||
| void ExternalUtilityThatCallsRecordProperty(const char* key, | ||||
|                                             const char* value) { | ||||
| void ExternalUtilityThatCallsRecordProperty(const std::string& key, | ||||
|                                             const std::string& value) { | ||||
|   testing::Test::RecordProperty(key, value); | ||||
| } | ||||
|  | ||||
| @@ -173,5 +176,6 @@ int main(int argc, char** argv) { | ||||
|     TestEventListeners& listeners = UnitTest::GetInstance()->listeners(); | ||||
|     delete listeners.Release(listeners.default_xml_generator()); | ||||
|   } | ||||
|   testing::Test::RecordProperty("ad_hoc_property", "42"); | ||||
|   return RUN_ALL_TESTS(); | ||||
| } | ||||
|   | ||||
| @@ -80,7 +80,9 @@ class GTestXMLTestCase(gtest_test_utils.TestCase): | ||||
|     actual_attributes   = actual_node  .attributes | ||||
|     self.assertEquals( | ||||
|         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): | ||||
|       expected_attr = expected_attributes.item(i) | ||||
|       actual_attr   = actual_attributes.get(expected_attr.name) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 vladlosev
					vladlosev