1 | #!/usr/bin/env python
|
---|
2 | #
|
---|
3 | # Copyright 2006, Google Inc.
|
---|
4 | # All rights reserved.
|
---|
5 | #
|
---|
6 | # Redistribution and use in source and binary forms, with or without
|
---|
7 | # modification, are permitted provided that the following conditions are
|
---|
8 | # met:
|
---|
9 | #
|
---|
10 | # * Redistributions of source code must retain the above copyright
|
---|
11 | # notice, this list of conditions and the following disclaimer.
|
---|
12 | # * Redistributions in binary form must reproduce the above
|
---|
13 | # copyright notice, this list of conditions and the following disclaimer
|
---|
14 | # in the documentation and/or other materials provided with the
|
---|
15 | # distribution.
|
---|
16 | # * Neither the name of Google Inc. nor the names of its
|
---|
17 | # contributors may be used to endorse or promote products derived from
|
---|
18 | # this software without specific prior written permission.
|
---|
19 | #
|
---|
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
---|
21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
---|
22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
---|
23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
---|
24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
---|
25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
---|
26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
---|
27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
---|
28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
---|
29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
---|
30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
---|
31 |
|
---|
32 | """Unit test for the gtest_xml_output module"""
|
---|
33 |
|
---|
34 | __author__ = 'eefacm@gmail.com (Sean Mcafee)'
|
---|
35 |
|
---|
36 | import datetime
|
---|
37 | import errno
|
---|
38 | import os
|
---|
39 | import re
|
---|
40 | import sys
|
---|
41 | from xml.dom import minidom, Node
|
---|
42 |
|
---|
43 | import gtest_test_utils
|
---|
44 | import gtest_xml_test_utils
|
---|
45 |
|
---|
46 |
|
---|
47 | GTEST_FILTER_FLAG = '--gtest_filter'
|
---|
48 | GTEST_LIST_TESTS_FLAG = '--gtest_list_tests'
|
---|
49 | GTEST_OUTPUT_FLAG = "--gtest_output"
|
---|
50 | GTEST_DEFAULT_OUTPUT_FILE = "test_detail.xml"
|
---|
51 | GTEST_PROGRAM_NAME = "gtest_xml_output_unittest_"
|
---|
52 |
|
---|
53 | SUPPORTS_STACK_TRACES = False
|
---|
54 |
|
---|
55 | if SUPPORTS_STACK_TRACES:
|
---|
56 | STACK_TRACE_TEMPLATE = '\nStack trace:\n*'
|
---|
57 | else:
|
---|
58 | STACK_TRACE_TEMPLATE = ''
|
---|
59 |
|
---|
60 | EXPECTED_NON_EMPTY_XML = """<?xml version="1.0" encoding="UTF-8"?>
|
---|
61 | <testsuites tests="23" failures="4" disabled="2" errors="0" time="*" timestamp="*" name="AllTests" ad_hoc_property="42">
|
---|
62 | <testsuite name="SuccessfulTest" tests="1" failures="0" disabled="0" errors="0" time="*">
|
---|
63 | <testcase name="Succeeds" status="run" time="*" classname="SuccessfulTest"/>
|
---|
64 | </testsuite>
|
---|
65 | <testsuite name="FailedTest" tests="1" failures="1" disabled="0" errors="0" time="*">
|
---|
66 | <testcase name="Fails" status="run" time="*" classname="FailedTest">
|
---|
67 | <failure message="gtest_xml_output_unittest_.cc:*
Value of: 2
Expected: 1" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
|
---|
68 | Value of: 2
|
---|
69 | Expected: 1%(stack)s]]></failure>
|
---|
70 | </testcase>
|
---|
71 | </testsuite>
|
---|
72 | <testsuite name="MixedResultTest" tests="3" failures="1" disabled="1" errors="0" time="*">
|
---|
73 | <testcase name="Succeeds" status="run" time="*" classname="MixedResultTest"/>
|
---|
74 | <testcase name="Fails" status="run" time="*" classname="MixedResultTest">
|
---|
75 | <failure message="gtest_xml_output_unittest_.cc:*
Value of: 2
Expected: 1" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
|
---|
76 | Value of: 2
|
---|
77 | Expected: 1%(stack)s]]></failure>
|
---|
78 | <failure message="gtest_xml_output_unittest_.cc:*
Value of: 3
Expected: 2" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
|
---|
79 | Value of: 3
|
---|
80 | Expected: 2%(stack)s]]></failure>
|
---|
81 | </testcase>
|
---|
82 | <testcase name="DISABLED_test" status="notrun" time="*" classname="MixedResultTest"/>
|
---|
83 | </testsuite>
|
---|
84 | <testsuite name="XmlQuotingTest" tests="1" failures="1" disabled="0" errors="0" time="*">
|
---|
85 | <testcase name="OutputsCData" status="run" time="*" classname="XmlQuotingTest">
|
---|
86 | <failure message="gtest_xml_output_unittest_.cc:*
Failed
XML output: <?xml encoding="utf-8"><top><![CDATA[cdata text]]></top>" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
|
---|
87 | Failed
|
---|
88 | XML output: <?xml encoding="utf-8"><top><![CDATA[cdata text]]>]]><![CDATA[</top>%(stack)s]]></failure>
|
---|
89 | </testcase>
|
---|
90 | </testsuite>
|
---|
91 | <testsuite name="InvalidCharactersTest" tests="1" failures="1" disabled="0" errors="0" time="*">
|
---|
92 | <testcase name="InvalidCharactersInMessage" status="run" time="*" classname="InvalidCharactersTest">
|
---|
93 | <failure message="gtest_xml_output_unittest_.cc:*
Failed
Invalid characters in brackets []" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
|
---|
94 | Failed
|
---|
95 | Invalid characters in brackets []%(stack)s]]></failure>
|
---|
96 | </testcase>
|
---|
97 | </testsuite>
|
---|
98 | <testsuite name="DisabledTest" tests="1" failures="0" disabled="1" errors="0" time="*">
|
---|
99 | <testcase name="DISABLED_test_not_run" status="notrun" time="*" classname="DisabledTest"/>
|
---|
100 | </testsuite>
|
---|
101 | <testsuite name="PropertyRecordingTest" tests="4" failures="0" disabled="0" errors="0" time="*" SetUpTestCase="yes" TearDownTestCase="aye">
|
---|
102 | <testcase name="OneProperty" status="run" time="*" classname="PropertyRecordingTest" key_1="1"/>
|
---|
103 | <testcase name="IntValuedProperty" status="run" time="*" classname="PropertyRecordingTest" key_int="1"/>
|
---|
104 | <testcase name="ThreeProperties" status="run" time="*" classname="PropertyRecordingTest" key_1="1" key_2="2" key_3="3"/>
|
---|
105 | <testcase name="TwoValuesForOneKeyUsesLastValue" status="run" time="*" classname="PropertyRecordingTest" key_1="2"/>
|
---|
106 | </testsuite>
|
---|
107 | <testsuite name="NoFixtureTest" tests="3" failures="0" disabled="0" errors="0" time="*">
|
---|
108 | <testcase name="RecordProperty" status="run" time="*" classname="NoFixtureTest" key="1"/>
|
---|
109 | <testcase name="ExternalUtilityThatCallsRecordIntValuedProperty" status="run" time="*" classname="NoFixtureTest" key_for_utility_int="1"/>
|
---|
110 | <testcase name="ExternalUtilityThatCallsRecordStringValuedProperty" status="run" time="*" classname="NoFixtureTest" key_for_utility_string="1"/>
|
---|
111 | </testsuite>
|
---|
112 | <testsuite name="Single/ValueParamTest" tests="4" failures="0" disabled="0" errors="0" time="*">
|
---|
113 | <testcase name="HasValueParamAttribute/0" value_param="33" status="run" time="*" classname="Single/ValueParamTest" />
|
---|
114 | <testcase name="HasValueParamAttribute/1" value_param="42" status="run" time="*" classname="Single/ValueParamTest" />
|
---|
115 | <testcase name="AnotherTestThatHasValueParamAttribute/0" value_param="33" status="run" time="*" classname="Single/ValueParamTest" />
|
---|
116 | <testcase name="AnotherTestThatHasValueParamAttribute/1" value_param="42" status="run" time="*" classname="Single/ValueParamTest" />
|
---|
117 | </testsuite>
|
---|
118 | <testsuite name="TypedTest/0" tests="1" failures="0" disabled="0" errors="0" time="*">
|
---|
119 | <testcase name="HasTypeParamAttribute" type_param="*" status="run" time="*" classname="TypedTest/0" />
|
---|
120 | </testsuite>
|
---|
121 | <testsuite name="TypedTest/1" tests="1" failures="0" disabled="0" errors="0" time="*">
|
---|
122 | <testcase name="HasTypeParamAttribute" type_param="*" status="run" time="*" classname="TypedTest/1" />
|
---|
123 | </testsuite>
|
---|
124 | <testsuite name="Single/TypeParameterizedTestCase/0" tests="1" failures="0" disabled="0" errors="0" time="*">
|
---|
125 | <testcase name="HasTypeParamAttribute" type_param="*" status="run" time="*" classname="Single/TypeParameterizedTestCase/0" />
|
---|
126 | </testsuite>
|
---|
127 | <testsuite name="Single/TypeParameterizedTestCase/1" tests="1" failures="0" disabled="0" errors="0" time="*">
|
---|
128 | <testcase name="HasTypeParamAttribute" type_param="*" status="run" time="*" classname="Single/TypeParameterizedTestCase/1" />
|
---|
129 | </testsuite>
|
---|
130 | </testsuites>""" % {'stack': STACK_TRACE_TEMPLATE}
|
---|
131 |
|
---|
132 | EXPECTED_FILTERED_TEST_XML = """<?xml version="1.0" encoding="UTF-8"?>
|
---|
133 | <testsuites tests="1" failures="0" disabled="0" errors="0" time="*"
|
---|
134 | timestamp="*" name="AllTests" ad_hoc_property="42">
|
---|
135 | <testsuite name="SuccessfulTest" tests="1" failures="0" disabled="0"
|
---|
136 | errors="0" time="*">
|
---|
137 | <testcase name="Succeeds" status="run" time="*" classname="SuccessfulTest"/>
|
---|
138 | </testsuite>
|
---|
139 | </testsuites>"""
|
---|
140 |
|
---|
141 | EXPECTED_EMPTY_XML = """<?xml version="1.0" encoding="UTF-8"?>
|
---|
142 | <testsuites tests="0" failures="0" disabled="0" errors="0" time="*"
|
---|
143 | timestamp="*" name="AllTests">
|
---|
144 | </testsuites>"""
|
---|
145 |
|
---|
146 | GTEST_PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath(GTEST_PROGRAM_NAME)
|
---|
147 |
|
---|
148 | SUPPORTS_TYPED_TESTS = 'TypedTest' in gtest_test_utils.Subprocess(
|
---|
149 | [GTEST_PROGRAM_PATH, GTEST_LIST_TESTS_FLAG], capture_stderr=False).output
|
---|
150 |
|
---|
151 |
|
---|
152 | class GTestXMLOutputUnitTest(gtest_xml_test_utils.GTestXMLTestCase):
|
---|
153 | """
|
---|
154 | Unit test for Google Test's XML output functionality.
|
---|
155 | """
|
---|
156 |
|
---|
157 | # This test currently breaks on platforms that do not support typed and
|
---|
158 | # type-parameterized tests, so we don't run it under them.
|
---|
159 | if SUPPORTS_TYPED_TESTS:
|
---|
160 | def testNonEmptyXmlOutput(self):
|
---|
161 | """
|
---|
162 | Runs a test program that generates a non-empty XML output, and
|
---|
163 | tests that the XML output is expected.
|
---|
164 | """
|
---|
165 | self._TestXmlOutput(GTEST_PROGRAM_NAME, EXPECTED_NON_EMPTY_XML, 1)
|
---|
166 |
|
---|
167 | def testEmptyXmlOutput(self):
|
---|
168 | """Verifies XML output for a Google Test binary without actual tests.
|
---|
169 |
|
---|
170 | Runs a test program that generates an empty XML output, and
|
---|
171 | tests that the XML output is expected.
|
---|
172 | """
|
---|
173 |
|
---|
174 | self._TestXmlOutput('gtest_no_test_unittest', EXPECTED_EMPTY_XML, 0)
|
---|
175 |
|
---|
176 | def testTimestampValue(self):
|
---|
177 | """Checks whether the timestamp attribute in the XML output is valid.
|
---|
178 |
|
---|
179 | Runs a test program that generates an empty XML output, and checks if
|
---|
180 | the timestamp attribute in the testsuites tag is valid.
|
---|
181 | """
|
---|
182 | actual = self._GetXmlOutput('gtest_no_test_unittest', [], 0)
|
---|
183 | date_time_str = actual.documentElement.getAttributeNode('timestamp').value
|
---|
184 | # datetime.strptime() is only available in Python 2.5+ so we have to
|
---|
185 | # parse the expected datetime manually.
|
---|
186 | match = re.match(r'(\d+)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)', date_time_str)
|
---|
187 | self.assertTrue(
|
---|
188 | re.match,
|
---|
189 | 'XML datettime string %s has incorrect format' % date_time_str)
|
---|
190 | date_time_from_xml = datetime.datetime(
|
---|
191 | year=int(match.group(1)), month=int(match.group(2)),
|
---|
192 | day=int(match.group(3)), hour=int(match.group(4)),
|
---|
193 | minute=int(match.group(5)), second=int(match.group(6)))
|
---|
194 |
|
---|
195 | time_delta = abs(datetime.datetime.now() - date_time_from_xml)
|
---|
196 | # timestamp value should be near the current local time
|
---|
197 | self.assertTrue(time_delta < datetime.timedelta(seconds=600),
|
---|
198 | 'time_delta is %s' % time_delta)
|
---|
199 | actual.unlink()
|
---|
200 |
|
---|
201 | def testDefaultOutputFile(self):
|
---|
202 | """
|
---|
203 | Confirms that Google Test produces an XML output file with the expected
|
---|
204 | default name if no name is explicitly specified.
|
---|
205 | """
|
---|
206 | output_file = os.path.join(gtest_test_utils.GetTempDir(),
|
---|
207 | GTEST_DEFAULT_OUTPUT_FILE)
|
---|
208 | gtest_prog_path = gtest_test_utils.GetTestExecutablePath(
|
---|
209 | 'gtest_no_test_unittest')
|
---|
210 | try:
|
---|
211 | os.remove(output_file)
|
---|
212 | except OSError, e:
|
---|
213 | if e.errno != errno.ENOENT:
|
---|
214 | raise
|
---|
215 |
|
---|
216 | p = gtest_test_utils.Subprocess(
|
---|
217 | [gtest_prog_path, '%s=xml' % GTEST_OUTPUT_FLAG],
|
---|
218 | working_dir=gtest_test_utils.GetTempDir())
|
---|
219 | self.assert_(p.exited)
|
---|
220 | self.assertEquals(0, p.exit_code)
|
---|
221 | self.assert_(os.path.isfile(output_file))
|
---|
222 |
|
---|
223 | def testSuppressedXmlOutput(self):
|
---|
224 | """
|
---|
225 | Tests that no XML file is generated if the default XML listener is
|
---|
226 | shut down before RUN_ALL_TESTS is invoked.
|
---|
227 | """
|
---|
228 |
|
---|
229 | xml_path = os.path.join(gtest_test_utils.GetTempDir(),
|
---|
230 | GTEST_PROGRAM_NAME + 'out.xml')
|
---|
231 | if os.path.isfile(xml_path):
|
---|
232 | os.remove(xml_path)
|
---|
233 |
|
---|
234 | command = [GTEST_PROGRAM_PATH,
|
---|
235 | '%s=xml:%s' % (GTEST_OUTPUT_FLAG, xml_path),
|
---|
236 | '--shut_down_xml']
|
---|
237 | p = gtest_test_utils.Subprocess(command)
|
---|
238 | if p.terminated_by_signal:
|
---|
239 | # p.signal is avalable only if p.terminated_by_signal is True.
|
---|
240 | self.assertFalse(
|
---|
241 | p.terminated_by_signal,
|
---|
242 | '%s was killed by signal %d' % (GTEST_PROGRAM_NAME, p.signal))
|
---|
243 | else:
|
---|
244 | self.assert_(p.exited)
|
---|
245 | self.assertEquals(1, p.exit_code,
|
---|
246 | "'%s' exited with code %s, which doesn't match "
|
---|
247 | 'the expected exit code %s.'
|
---|
248 | % (command, p.exit_code, 1))
|
---|
249 |
|
---|
250 | self.assert_(not os.path.isfile(xml_path))
|
---|
251 |
|
---|
252 | def testFilteredTestXmlOutput(self):
|
---|
253 | """Verifies XML output when a filter is applied.
|
---|
254 |
|
---|
255 | Runs a test program that executes only some tests and verifies that
|
---|
256 | non-selected tests do not show up in the XML output.
|
---|
257 | """
|
---|
258 |
|
---|
259 | self._TestXmlOutput(GTEST_PROGRAM_NAME, EXPECTED_FILTERED_TEST_XML, 0,
|
---|
260 | extra_args=['%s=SuccessfulTest.*' % GTEST_FILTER_FLAG])
|
---|
261 |
|
---|
262 | def _GetXmlOutput(self, gtest_prog_name, extra_args, expected_exit_code):
|
---|
263 | """
|
---|
264 | Returns the xml output generated by running the program gtest_prog_name.
|
---|
265 | Furthermore, the program's exit code must be expected_exit_code.
|
---|
266 | """
|
---|
267 | xml_path = os.path.join(gtest_test_utils.GetTempDir(),
|
---|
268 | gtest_prog_name + 'out.xml')
|
---|
269 | gtest_prog_path = gtest_test_utils.GetTestExecutablePath(gtest_prog_name)
|
---|
270 |
|
---|
271 | command = ([gtest_prog_path, '%s=xml:%s' % (GTEST_OUTPUT_FLAG, xml_path)] +
|
---|
272 | extra_args)
|
---|
273 | p = gtest_test_utils.Subprocess(command)
|
---|
274 | if p.terminated_by_signal:
|
---|
275 | self.assert_(False,
|
---|
276 | '%s was killed by signal %d' % (gtest_prog_name, p.signal))
|
---|
277 | else:
|
---|
278 | self.assert_(p.exited)
|
---|
279 | self.assertEquals(expected_exit_code, p.exit_code,
|
---|
280 | "'%s' exited with code %s, which doesn't match "
|
---|
281 | 'the expected exit code %s.'
|
---|
282 | % (command, p.exit_code, expected_exit_code))
|
---|
283 | actual = minidom.parse(xml_path)
|
---|
284 | return actual
|
---|
285 |
|
---|
286 | def _TestXmlOutput(self, gtest_prog_name, expected_xml,
|
---|
287 | expected_exit_code, extra_args=None):
|
---|
288 | """
|
---|
289 | Asserts that the XML document generated by running the program
|
---|
290 | gtest_prog_name matches expected_xml, a string containing another
|
---|
291 | XML document. Furthermore, the program's exit code must be
|
---|
292 | expected_exit_code.
|
---|
293 | """
|
---|
294 |
|
---|
295 | actual = self._GetXmlOutput(gtest_prog_name, extra_args or [],
|
---|
296 | expected_exit_code)
|
---|
297 | expected = minidom.parseString(expected_xml)
|
---|
298 | self.NormalizeXml(actual.documentElement)
|
---|
299 | self.AssertEquivalentNodes(expected.documentElement,
|
---|
300 | actual.documentElement)
|
---|
301 | expected.unlink()
|
---|
302 | actual.unlink()
|
---|
303 |
|
---|
304 |
|
---|
305 | if __name__ == '__main__':
|
---|
306 | os.environ['GTEST_STACK_TRACE_DEPTH'] = '1'
|
---|
307 | gtest_test_utils.Main()
|
---|