]>
Commit | Line | Data |
---|---|---|
31f18b77 FG |
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:*
 Expected: 1
To be equal to: 2" type=""><![CDATA[gtest_xml_output_unittest_.cc:* | |
68 | Expected: 1 | |
69 | To be equal to: 2%(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:*
 Expected: 1
To be equal to: 2" type=""><![CDATA[gtest_xml_output_unittest_.cc:* | |
76 | Expected: 1 | |
77 | To be equal to: 2%(stack)s]]></failure> | |
78 | <failure message="gtest_xml_output_unittest_.cc:*
 Expected: 2
To be equal to: 3" type=""><![CDATA[gtest_xml_output_unittest_.cc:* | |
79 | Expected: 2 | |
80 | To be equal to: 3%(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: | |
213 | e = sys.exc_info()[1] | |
214 | if e.errno != errno.ENOENT: | |
215 | raise | |
216 | ||
217 | p = gtest_test_utils.Subprocess( | |
218 | [gtest_prog_path, '%s=xml' % GTEST_OUTPUT_FLAG], | |
219 | working_dir=gtest_test_utils.GetTempDir()) | |
220 | self.assert_(p.exited) | |
221 | self.assertEquals(0, p.exit_code) | |
222 | self.assert_(os.path.isfile(output_file)) | |
223 | ||
224 | def testSuppressedXmlOutput(self): | |
225 | """ | |
226 | Tests that no XML file is generated if the default XML listener is | |
227 | shut down before RUN_ALL_TESTS is invoked. | |
228 | """ | |
229 | ||
230 | xml_path = os.path.join(gtest_test_utils.GetTempDir(), | |
231 | GTEST_PROGRAM_NAME + 'out.xml') | |
232 | if os.path.isfile(xml_path): | |
233 | os.remove(xml_path) | |
234 | ||
235 | command = [GTEST_PROGRAM_PATH, | |
236 | '%s=xml:%s' % (GTEST_OUTPUT_FLAG, xml_path), | |
237 | '--shut_down_xml'] | |
238 | p = gtest_test_utils.Subprocess(command) | |
239 | if p.terminated_by_signal: | |
240 | # p.signal is avalable only if p.terminated_by_signal is True. | |
241 | self.assertFalse( | |
242 | p.terminated_by_signal, | |
243 | '%s was killed by signal %d' % (GTEST_PROGRAM_NAME, p.signal)) | |
244 | else: | |
245 | self.assert_(p.exited) | |
246 | self.assertEquals(1, p.exit_code, | |
247 | "'%s' exited with code %s, which doesn't match " | |
248 | 'the expected exit code %s.' | |
249 | % (command, p.exit_code, 1)) | |
250 | ||
251 | self.assert_(not os.path.isfile(xml_path)) | |
252 | ||
253 | def testFilteredTestXmlOutput(self): | |
254 | """Verifies XML output when a filter is applied. | |
255 | ||
256 | Runs a test program that executes only some tests and verifies that | |
257 | non-selected tests do not show up in the XML output. | |
258 | """ | |
259 | ||
260 | self._TestXmlOutput(GTEST_PROGRAM_NAME, EXPECTED_FILTERED_TEST_XML, 0, | |
261 | extra_args=['%s=SuccessfulTest.*' % GTEST_FILTER_FLAG]) | |
262 | ||
263 | def _GetXmlOutput(self, gtest_prog_name, extra_args, expected_exit_code): | |
264 | """ | |
265 | Returns the xml output generated by running the program gtest_prog_name. | |
266 | Furthermore, the program's exit code must be expected_exit_code. | |
267 | """ | |
268 | xml_path = os.path.join(gtest_test_utils.GetTempDir(), | |
269 | gtest_prog_name + 'out.xml') | |
270 | gtest_prog_path = gtest_test_utils.GetTestExecutablePath(gtest_prog_name) | |
271 | ||
272 | command = ([gtest_prog_path, '%s=xml:%s' % (GTEST_OUTPUT_FLAG, xml_path)] + | |
273 | extra_args) | |
274 | p = gtest_test_utils.Subprocess(command) | |
275 | if p.terminated_by_signal: | |
276 | self.assert_(False, | |
277 | '%s was killed by signal %d' % (gtest_prog_name, p.signal)) | |
278 | else: | |
279 | self.assert_(p.exited) | |
280 | self.assertEquals(expected_exit_code, p.exit_code, | |
281 | "'%s' exited with code %s, which doesn't match " | |
282 | 'the expected exit code %s.' | |
283 | % (command, p.exit_code, expected_exit_code)) | |
284 | actual = minidom.parse(xml_path) | |
285 | return actual | |
286 | ||
287 | def _TestXmlOutput(self, gtest_prog_name, expected_xml, | |
288 | expected_exit_code, extra_args=None): | |
289 | """ | |
290 | Asserts that the XML document generated by running the program | |
291 | gtest_prog_name matches expected_xml, a string containing another | |
292 | XML document. Furthermore, the program's exit code must be | |
293 | expected_exit_code. | |
294 | """ | |
295 | ||
296 | actual = self._GetXmlOutput(gtest_prog_name, extra_args or [], | |
297 | expected_exit_code) | |
298 | expected = minidom.parseString(expected_xml) | |
299 | self.NormalizeXml(actual.documentElement) | |
300 | self.AssertEquivalentNodes(expected.documentElement, | |
301 | actual.documentElement) | |
302 | expected.unlink() | |
303 | actual.unlink() | |
304 | ||
305 | ||
306 | if __name__ == '__main__': | |
307 | os.environ['GTEST_STACK_TRACE_DEPTH'] = '1' | |
308 | gtest_test_utils.Main() |