Implements the timestamp attribute for the testsuites element in the output XML (external contribution by Dirk Meister).
This commit is contained in:
parent
69a40b7d4a
commit
431a8be166
|
@ -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;
|
||||
|
||||
|
|
|
@ -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_;
|
||||
|
||||
|
|
38
src/gtest.cc
38
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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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__':
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user