summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvladlosev <vladlosev@861a406c-534a-0410-8894-cb66d6ee9925>2011-10-05 05:52:34 +0000
committervladlosev <vladlosev@861a406c-534a-0410-8894-cb66d6ee9925>2011-10-05 05:52:34 +0000
commit8883b42f2a78976dde09f31ca72806072e33bd5f (patch)
treedd74052e6e0f73cf9d68b55dab4f438b33c57820
parente35019652516ad3b4e9bd8c49dbcfca9d443d7e4 (diff)
downloadgoogletest-8883b42f2a78976dde09f31ca72806072e33bd5f.tar.gz
Implements the timestamp attribute for the testsuites element in the output XML (external contribution by Dirk Meister).
git-svn-id: http://googletest.googlecode.com/svn/trunk@600 861a406c-534a-0410-8894-cb66d6ee9925
-rw-r--r--include/gtest/gtest.h4
-rw-r--r--src/gtest-internal-inl.h14
-rw-r--r--src/gtest.cc38
-rw-r--r--test/gtest_unittest.cc106
-rwxr-xr-xtest/gtest_xml_outfiles_test.py4
-rwxr-xr-xtest/gtest_xml_output_unittest.py64
-rwxr-xr-xtest/gtest_xml_test_utils.py54
7 files changed, 243 insertions, 41 deletions
diff --git a/include/gtest/gtest.h b/include/gtest/gtest.h
index cd01c7b..432ee2d 100644
--- a/include/gtest/gtest.h
+++ b/include/gtest/gtest.h
@@ -1152,6 +1152,10 @@ class GTEST_API_ UnitTest {
// Gets the number of tests that should run.
int test_to_run_count() const;
+ // Gets the time of the test program start, in ms from the start of the
+ // UNIX epoch.
+ TimeInMillis start_timestamp() const;
+
// Gets the elapsed time, in milliseconds.
TimeInMillis elapsed_time() const;
diff --git a/src/gtest-internal-inl.h b/src/gtest-internal-inl.h
index 65a2101..8a85724 100644
--- a/src/gtest-internal-inl.h
+++ b/src/gtest-internal-inl.h
@@ -112,6 +112,12 @@ GTEST_API_ bool ShouldUseColor(bool stdout_is_tty);
// Formats the given time in milliseconds as seconds.
GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms);
+// Converts the given time in milliseconds to a date string in the ISO 8601
+// format, without the timezone information. N.B.: due to the use the
+// non-reentrant localtime() function, this function is not thread safe. Do
+// not use it in any code that can be called from multiple threads.
+GTEST_API_ std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms);
+
// Parses a string for an Int32 flag, in the form of "--flag=value".
//
// On success, stores the value of the flag in *value, and returns
@@ -548,6 +554,10 @@ class GTEST_API_ UnitTestImpl {
// Gets the number of tests that should run.
int test_to_run_count() const;
+ // Gets the time of the test program start, in ms from the start of the
+ // UNIX epoch.
+ TimeInMillis start_timestamp() const { return start_timestamp_; }
+
// Gets the elapsed time, in milliseconds.
TimeInMillis elapsed_time() const { return elapsed_time_; }
@@ -880,6 +890,10 @@ class GTEST_API_ UnitTestImpl {
// Our random number generator.
internal::Random random_;
+ // The time of the test program start, in ms from the start of the
+ // UNIX epoch.
+ TimeInMillis start_timestamp_;
+
// How long the test took to run, in milliseconds.
TimeInMillis elapsed_time_;
diff --git a/src/gtest.cc b/src/gtest.cc
index 6f7216f..7bdd28a 100644
--- a/src/gtest.cc
+++ b/src/gtest.cc
@@ -39,6 +39,7 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
+#include <time.h>
#include <wchar.h>
#include <wctype.h>
@@ -3195,6 +3196,32 @@ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) {
return ss.str();
}
+// Converts the given epoch time in milliseconds to a date string in the ISO
+// 8601 format, without the timezone information.
+std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) {
+ // Using non-reentrant version as localtime_r is not portable.
+ time_t seconds = static_cast<time_t>(ms / 1000);
+#ifdef _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4996) // Temporarily disables warning 4996
+ // (function or variable may be unsafe).
+ const struct tm* const time_struct = localtime(&seconds); // NOLINT
+# pragma warning(pop) // Restores the warning state again.
+#else
+ const struct tm* const time_struct = localtime(&seconds); // NOLINT
+#endif
+ if (time_struct == NULL)
+ return ""; // Invalid ms value
+
+ return String::Format("%d-%02d-%02dT%02d:%02d:%02d", // YYYY-MM-DDThh:mm:ss
+ time_struct->tm_year + 1900,
+ time_struct->tm_mon + 1,
+ time_struct->tm_mday,
+ time_struct->tm_hour,
+ time_struct->tm_min,
+ time_struct->tm_sec);
+}
+
// Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream,
const char* data) {
@@ -3291,10 +3318,11 @@ void XmlUnitTestResultPrinter::PrintXmlUnitTest(FILE* out,
fprintf(out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
fprintf(out,
"<testsuites tests=\"%d\" failures=\"%d\" disabled=\"%d\" "
- "errors=\"0\" time=\"%s\" ",
+ "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());
if (GTEST_FLAG(shuffle)) {
fprintf(out, "random_seed=\"%d\" ", unit_test.random_seed());
@@ -3687,6 +3715,12 @@ int UnitTest::total_test_count() const { return impl()->total_test_count(); }
// Gets the number of tests that should run.
int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); }
+// Gets the time of the test program start, in ms from the start of the
+// UNIX epoch.
+internal::TimeInMillis UnitTest::start_timestamp() const {
+ return impl()->start_timestamp();
+}
+
// Gets the elapsed time, in milliseconds.
internal::TimeInMillis UnitTest::elapsed_time() const {
return impl()->elapsed_time();
@@ -3961,6 +3995,7 @@ UnitTestImpl::UnitTestImpl(UnitTest* parent)
post_flag_parse_init_performed_(false),
random_seed_(0), // Will be overridden by the flag before first use.
random_(0), // Will be reseeded before first use.
+ start_timestamp_(0),
elapsed_time_(0),
#if GTEST_HAS_DEATH_TEST
internal_run_death_test_flag_(NULL),
@@ -4192,6 +4227,7 @@ bool UnitTestImpl::RunAllTests() {
TestEventListener* repeater = listeners()->repeater();
+ start_timestamp_ = GetTimeInMillis();
repeater->OnTestProgramStart(*parent_);
// How many times to repeat the tests? We don't want to repeat them
diff --git a/test/gtest_unittest.cc b/test/gtest_unittest.cc
index 23d6860..0d624fd 100644
--- a/test/gtest_unittest.cc
+++ b/test/gtest_unittest.cc
@@ -71,6 +71,7 @@ TEST(CommandLineFlagsTest, CanBeAccessedInCodeOnceGTestHIsIncluded) {
#include <limits.h> // For INT_MAX.
#include <stdlib.h>
+#include <string.h>
#include <time.h>
#include <map>
@@ -141,6 +142,7 @@ using testing::TestPartResult;
using testing::TestPartResultArray;
using testing::TestProperty;
using testing::TestResult;
+using testing::TimeInMillis;
using testing::UnitTest;
using testing::kMaxStackTraceDepth;
using testing::internal::AddReference;
@@ -156,6 +158,7 @@ using testing::internal::CountIf;
using testing::internal::EqFailure;
using testing::internal::FloatingPoint;
using testing::internal::ForEach;
+using testing::internal::FormatEpochTimeInMillisAsIso8601;
using testing::internal::FormatTimeInMillisAsSeconds;
using testing::internal::GTestFlagSaver;
using testing::internal::GetCurrentOsStackTraceExceptTop;
@@ -163,6 +166,7 @@ using testing::internal::GetElementOr;
using testing::internal::GetNextRandomSeed;
using testing::internal::GetRandomSeedFromFlag;
using testing::internal::GetTestTypeId;
+using testing::internal::GetTimeInMillis;
using testing::internal::GetTypeId;
using testing::internal::GetUnitTestImpl;
using testing::internal::ImplicitlyConvertible;
@@ -308,6 +312,103 @@ TEST(FormatTimeInMillisAsSecondsTest, FormatsNegativeNumber) {
EXPECT_EQ("-3", FormatTimeInMillisAsSeconds(-3000));
}
+// Tests FormatEpochTimeInMillisAsIso8601(). The correctness of conversion
+// for particular dates below was verified in Python using
+// datetime.datetime.fromutctimestamp(<timetamp>/1000).
+
+// FormatEpochTimeInMillisAsIso8601 depends on the current timezone, so we
+// have to set up a particular timezone to obtain predictable results.
+class FormatEpochTimeInMillisAsIso8601Test : public Test {
+ public:
+ // On Cygwin, GCC doesn't allow unqualified integer literals to exceed
+ // 32 bits, even when 64-bit integer types are available. We have to
+ // force the constants to have a 64-bit type here.
+ static const TimeInMillis kMillisPerSec = 1000;
+
+ private:
+ virtual void SetUp() {
+ saved_tz_ = NULL;
+#if _MSC_VER
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4996) // Temporarily disables warning 4996
+ // (function or variable may be unsafe
+ // for getenv, function is deprecated for
+ // strdup).
+ if (getenv("TZ"))
+ saved_tz_ = strdup(getenv("TZ"));
+# pragma warning(pop) // Restores the warning state again.
+#else
+ if (getenv("TZ"))
+ saved_tz_ = strdup(getenv("TZ"));
+#endif
+
+ // Set up the time zone for FormatEpochTimeInMillisAsIso8601 to use. We
+ // cannot use the local time zone because the function's output depends
+ // on the time zone.
+ SetTimeZone("UTC+00");
+ }
+
+ virtual void TearDown() {
+ SetTimeZone(saved_tz_);
+ free(const_cast<char*>(saved_tz_));
+ saved_tz_ = NULL;
+ }
+
+ static void SetTimeZone(const char* time_zone) {
+ // tzset() distinguishes between the TZ variable being present and empty
+ // and not being present, so we have to consider the case of time_zone
+ // being NULL.
+#if _MSC_VER
+ // ...Unless it's MSVC, whose standard library's _putenv doesn't
+ // distinguish between an empty and a missing variable.
+ const std::string env_var =
+ std::string("TZ=") + (time_zone ? time_zone : "");
+ _putenv(env_var.c_str());
+# pragma warning(push) // Saves the current warning state.
+# pragma warning(disable:4996) // Temporarily disables warning 4996
+ // (function is deprecated).
+ tzset();
+# pragma warning(pop) // Restores the warning state again.
+#else
+ if (time_zone) {
+ setenv(("TZ"), time_zone, 1);
+ } else {
+ unsetenv("TZ");
+ }
+ tzset();
+#endif
+ }
+
+ const char* saved_tz_;
+};
+
+const TimeInMillis FormatEpochTimeInMillisAsIso8601Test::kMillisPerSec;
+
+TEST_F(FormatEpochTimeInMillisAsIso8601Test, PrintsTwoDigitSegments) {
+ EXPECT_EQ("2011-10-31T18:52:42",
+ FormatEpochTimeInMillisAsIso8601(1320087162 * kMillisPerSec));
+}
+
+TEST_F(FormatEpochTimeInMillisAsIso8601Test, MillisecondsDoNotAffectResult) {
+ EXPECT_EQ(
+ "2011-10-31T18:52:42",
+ FormatEpochTimeInMillisAsIso8601(1320087162 * kMillisPerSec + 234));
+}
+
+TEST_F(FormatEpochTimeInMillisAsIso8601Test, PrintsLeadingZeroes) {
+ EXPECT_EQ("2011-09-03T05:07:02",
+ FormatEpochTimeInMillisAsIso8601(1315026422 * kMillisPerSec));
+}
+
+TEST_F(FormatEpochTimeInMillisAsIso8601Test, Prints24HourTime) {
+ EXPECT_EQ("2011-09-28T17:08:22",
+ FormatEpochTimeInMillisAsIso8601(1317229702 * kMillisPerSec));
+}
+
+TEST_F(FormatEpochTimeInMillisAsIso8601Test, PrintsEpochStart) {
+ EXPECT_EQ("1970-01-01T00:00:00", FormatEpochTimeInMillisAsIso8601(0));
+}
+
#if GTEST_CAN_COMPARE_NULL
# ifdef __BORLANDC__
@@ -2130,6 +2231,11 @@ TEST(UnitTestTest, CanGetOriginalWorkingDir) {
EXPECT_STRNE(UnitTest::GetInstance()->original_working_dir(), "");
}
+TEST(UnitTestTest, ReturnsPlausibleTimestamp) {
+ EXPECT_LT(0, UnitTest::GetInstance()->start_timestamp());
+ EXPECT_LE(UnitTest::GetInstance()->start_timestamp(), GetTimeInMillis());
+}
+
// 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.
diff --git a/test/gtest_xml_outfiles_test.py b/test/gtest_xml_outfiles_test.py
index 0fe947f..524e437 100755
--- a/test/gtest_xml_outfiles_test.py
+++ b/test/gtest_xml_outfiles_test.py
@@ -45,7 +45,7 @@ GTEST_OUTPUT_1_TEST = "gtest_xml_outfile1_test_"
GTEST_OUTPUT_2_TEST = "gtest_xml_outfile2_test_"
EXPECTED_XML_1 = """<?xml version="1.0" encoding="UTF-8"?>
-<testsuites tests="1" failures="0" disabled="0" errors="0" time="*" name="AllTests">
+<testsuites tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*" name="AllTests">
<testsuite name="PropertyOne" tests="1" failures="0" disabled="0" errors="0" time="*">
<testcase name="TestSomeProperties" status="run" time="*" classname="PropertyOne" SetUpProp="1" TestSomeProperty="1" TearDownProp="1" />
</testsuite>
@@ -53,7 +53,7 @@ EXPECTED_XML_1 = """<?xml version="1.0" encoding="UTF-8"?>
"""
EXPECTED_XML_2 = """<?xml version="1.0" encoding="UTF-8"?>
-<testsuites tests="1" failures="0" disabled="0" errors="0" time="*" name="AllTests">
+<testsuites tests="1" failures="0" disabled="0" errors="0" time="*" timestamp="*" name="AllTests">
<testsuite name="PropertyTwo" tests="1" failures="0" disabled="0" errors="0" time="*">
<testcase name="TestSomeProperties" status="run" time="*" classname="PropertyTwo" SetUpProp="2" TestSomeProperty="2" TearDownProp="2" />
</testsuite>
diff --git a/test/gtest_xml_output_unittest.py b/test/gtest_xml_output_unittest.py
index 06637e5..8390300 100755
--- a/test/gtest_xml_output_unittest.py
+++ b/test/gtest_xml_output_unittest.py
@@ -33,8 +33,10 @@
__author__ = 'eefacm@gmail.com (Sean Mcafee)'
+import datetime
import errno
import os
+import re
import sys
from xml.dom import minidom, Node
@@ -55,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="*" name="AllTests">
+<testsuites tests="23" failures="4" disabled="2" errors="0" time="*" timestamp="*" name="AllTests">
<testsuite name="SuccessfulTest" tests="1" failures="0" disabled="0" errors="0" time="*">
<testcase name="Succeeds" status="run" time="*" classname="SuccessfulTest"/>
</testsuite>
@@ -128,7 +130,7 @@ Invalid characters in brackets []%(stack)s]]></failure>
EXPECTED_EMPTY_XML = """<?xml version="1.0" encoding="UTF-8"?>
-<testsuites tests="0" failures="0" disabled="0" errors="0" time="*" name="AllTests">
+<testsuites tests="0" failures="0" disabled="0" errors="0" time="*" timestamp="*" name="AllTests">
</testsuites>"""
GTEST_PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath(GTEST_PROGRAM_NAME)
@@ -153,13 +155,39 @@ class GTestXMLOutputUnitTest(gtest_xml_test_utils.GTestXMLTestCase):
self._TestXmlOutput(GTEST_PROGRAM_NAME, EXPECTED_NON_EMPTY_XML, 1)
def testEmptyXmlOutput(self):
- """
+ """Verifies XML output for a Google Test binary without actual tests.
+
Runs a test program that generates an empty XML output, and
tests that the XML output is expected.
"""
self._TestXmlOutput('gtest_no_test_unittest', EXPECTED_EMPTY_XML, 0)
+ def testTimestampValue(self):
+ """Checks whether the timestamp attribute in the XML output is valid.
+
+ Runs a test program that generates an empty XML output, and checks if
+ the timestamp attribute in the testsuites tag is valid.
+ """
+ actual = self._GetXmlOutput('gtest_no_test_unittest', 0)
+ date_time_str = actual.documentElement.getAttributeNode('timestamp').value
+ # datetime.strptime() is only available in Python 2.5+ so we have to
+ # parse the expected datetime manually.
+ match = re.match(r'(\d+)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)', date_time_str)
+ self.assertTrue(
+ re.match,
+ 'XML datettime string %s has incorrect format' % date_time_str)
+ date_time_from_xml = datetime.datetime(
+ year=int(match.group(1)), month=int(match.group(2)),
+ day=int(match.group(3)), hour=int(match.group(4)),
+ minute=int(match.group(5)), second=int(match.group(6)))
+
+ time_delta = abs(datetime.datetime.now() - date_time_from_xml)
+ # timestamp value should be near the current local time
+ self.assertTrue(time_delta < datetime.timedelta(seconds=600),
+ 'time_delta is %s' % time_delta)
+ actual.unlink()
+
def testDefaultOutputFile(self):
"""
Confirms that Google Test produces an XML output file with the expected
@@ -198,8 +226,10 @@ class GTestXMLOutputUnitTest(gtest_xml_test_utils.GTestXMLTestCase):
'--shut_down_xml']
p = gtest_test_utils.Subprocess(command)
if p.terminated_by_signal:
- self.assert_(False,
- '%s was killed by signal %d' % (gtest_prog_name, p.signal))
+ # p.signal is avalable only if p.terminated_by_signal is True.
+ self.assertFalse(
+ p.terminated_by_signal,
+ '%s was killed by signal %d' % (GTEST_PROGRAM_NAME, p.signal))
else:
self.assert_(p.exited)
self.assertEquals(1, p.exit_code,
@@ -209,13 +239,10 @@ class GTestXMLOutputUnitTest(gtest_xml_test_utils.GTestXMLTestCase):
self.assert_(not os.path.isfile(xml_path))
-
- def _TestXmlOutput(self, gtest_prog_name, expected_xml, expected_exit_code):
+ def _GetXmlOutput(self, gtest_prog_name, expected_exit_code):
"""
- Asserts that the XML document generated by running the program
- gtest_prog_name matches expected_xml, a string containing another
- XML document. Furthermore, the program's exit code must be
- expected_exit_code.
+ Returns the xml output generated by running the program gtest_prog_name.
+ Furthermore, the program's exit code must be expected_exit_code.
"""
xml_path = os.path.join(gtest_test_utils.GetTempDir(),
gtest_prog_name + 'out.xml')
@@ -232,15 +259,24 @@ class GTestXMLOutputUnitTest(gtest_xml_test_utils.GTestXMLTestCase):
"'%s' exited with code %s, which doesn't match "
'the expected exit code %s.'
% (command, p.exit_code, expected_exit_code))
+ actual = minidom.parse(xml_path)
+ return actual
+ def _TestXmlOutput(self, gtest_prog_name, expected_xml, expected_exit_code):
+ """
+ Asserts that the XML document generated by running the program
+ gtest_prog_name matches expected_xml, a string containing another
+ XML document. Furthermore, the program's exit code must be
+ expected_exit_code.
+ """
+
+ actual = self._GetXmlOutput(gtest_prog_name, expected_exit_code)
expected = minidom.parseString(expected_xml)
- actual = minidom.parse(xml_path)
self.NormalizeXml(actual.documentElement)
self.AssertEquivalentNodes(expected.documentElement,
actual.documentElement)
expected.unlink()
- actual .unlink()
-
+ actual.unlink()
if __name__ == '__main__':
diff --git a/test/gtest_xml_test_utils.py b/test/gtest_xml_test_utils.py
index 0f55c16..f94d634 100755
--- a/test/gtest_xml_test_utils.py
+++ b/test/gtest_xml_test_utils.py
@@ -39,8 +39,8 @@ from xml.dom import minidom, Node
import gtest_test_utils
-GTEST_OUTPUT_FLAG = "--gtest_output"
-GTEST_DEFAULT_OUTPUT_FILE = "test_detail.xml"
+GTEST_OUTPUT_FLAG = '--gtest_output'
+GTEST_DEFAULT_OUTPUT_FILE = 'test_detail.xml'
class GTestXMLTestCase(gtest_test_utils.TestCase):
"""
@@ -80,23 +80,23 @@ 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 ' + actual_node.tagName)
for i in range(expected_attributes.length):
expected_attr = expected_attributes.item(i)
actual_attr = actual_attributes.get(expected_attr.name)
self.assert_(
actual_attr is not None,
- "expected attribute %s not found in element %s" %
+ 'expected attribute %s not found in element %s' %
(expected_attr.name, actual_node.tagName))
self.assertEquals(expected_attr.value, actual_attr.value,
- " values of attribute %s in element %s differ" %
+ ' values of attribute %s in element %s differ' %
(expected_attr.name, actual_node.tagName))
expected_children = self._GetChildren(expected_node)
actual_children = self._GetChildren(actual_node)
self.assertEquals(
len(expected_children), len(actual_children),
- "number of child elements differ in element " + actual_node.tagName)
+ 'number of child elements differ in element ' + actual_node.tagName)
for child_id, child in expected_children.iteritems():
self.assert_(child_id in actual_children,
'<%s> is not in <%s> (in element %s)' %
@@ -104,10 +104,10 @@ class GTestXMLTestCase(gtest_test_utils.TestCase):
self.AssertEquivalentNodes(child, actual_children[child_id])
identifying_attribute = {
- "testsuites": "name",
- "testsuite": "name",
- "testcase": "name",
- "failure": "message",
+ 'testsuites': 'name',
+ 'testsuite': 'name',
+ 'testcase': 'name',
+ 'failure': 'message',
}
def _GetChildren(self, element):
@@ -127,20 +127,20 @@ class GTestXMLTestCase(gtest_test_utils.TestCase):
for child in element.childNodes:
if child.nodeType == Node.ELEMENT_NODE:
self.assert_(child.tagName in self.identifying_attribute,
- "Encountered unknown element <%s>" % child.tagName)
+ 'Encountered unknown element <%s>' % child.tagName)
childID = child.getAttribute(self.identifying_attribute[child.tagName])
self.assert_(childID not in children)
children[childID] = child
elif child.nodeType in [Node.TEXT_NODE, Node.CDATA_SECTION_NODE]:
- if "detail" not in children:
+ if 'detail' not in children:
if (child.nodeType == Node.CDATA_SECTION_NODE or
not child.nodeValue.isspace()):
- children["detail"] = child.ownerDocument.createCDATASection(
+ children['detail'] = child.ownerDocument.createCDATASection(
child.nodeValue)
else:
- children["detail"].nodeValue += child.nodeValue
+ children['detail'].nodeValue += child.nodeValue
else:
- self.fail("Encountered unexpected node type %d" % child.nodeType)
+ self.fail('Encountered unexpected node type %d' % child.nodeType)
return children
def NormalizeXml(self, element):
@@ -151,6 +151,8 @@ class GTestXMLTestCase(gtest_test_utils.TestCase):
* The "time" attribute of <testsuites>, <testsuite> and <testcase>
elements is replaced with a single asterisk, if it contains
only digit characters.
+ * The "timestamp" attribute of <testsuites> elements is replaced with a
+ single asterisk, if it contains a valid ISO8601 datetime value.
* The "type_param" attribute of <testcase> elements is replaced with a
single asterisk (if it sn non-empty) as it is the type name returned
by the compiler and is platform dependent.
@@ -160,20 +162,24 @@ class GTestXMLTestCase(gtest_test_utils.TestCase):
* The stack traces are removed.
"""
- if element.tagName in ("testsuites", "testsuite", "testcase"):
- time = element.getAttributeNode("time")
- time.value = re.sub(r"^\d+(\.\d+)?$", "*", time.value)
- type_param = element.getAttributeNode("type_param")
+ if element.tagName == 'testsuites':
+ timestamp = element.getAttributeNode('timestamp')
+ timestamp.value = re.sub(r'^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d$',
+ '*', timestamp.value)
+ if element.tagName in ('testsuites', 'testsuite', 'testcase'):
+ time = element.getAttributeNode('time')
+ time.value = re.sub(r'^\d+(\.\d+)?$', '*', time.value)
+ type_param = element.getAttributeNode('type_param')
if type_param and type_param.value:
- type_param.value = "*"
- elif element.tagName == "failure":
+ type_param.value = '*'
+ elif element.tagName == 'failure':
for child in element.childNodes:
if child.nodeType == Node.CDATA_SECTION_NODE:
# Removes the source line number.
- cdata = re.sub(r"^.*[/\\](.*:)\d+\n", "\\1*\n", child.nodeValue)
+ cdata = re.sub(r'^.*[/\\](.*:)\d+\n', '\\1*\n', child.nodeValue)
# Removes the actual stack trace.
- child.nodeValue = re.sub(r"\nStack trace:\n(.|\n)*",
- "", cdata)
+ child.nodeValue = re.sub(r'\nStack trace:\n(.|\n)*',
+ '', cdata)
for child in element.childNodes:
if child.nodeType == Node.ELEMENT_NODE:
self.NormalizeXml(child)