diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/gtest-internal-inl.h | 12 | ||||
| -rw-r--r-- | src/gtest.cc | 370 | 
2 files changed, 270 insertions, 112 deletions
| diff --git a/src/gtest-internal-inl.h b/src/gtest-internal-inl.h index d6610430..302b6cdb 100644 --- a/src/gtest-internal-inl.h +++ b/src/gtest-internal-inl.h @@ -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) { diff --git a/src/gtest.cc b/src/gtest.cc index 911919d8..5e96f3ec 100644 --- a/src/gtest.cc +++ b/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 << "'"; -          else -            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; -        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. | 
