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
34import datetime
35import errno
36import os
37import re
38import sys
39from xml.dom import minidom, Node
40
41from googletest.test import gtest_test_utils
42from googletest.test import gtest_xml_test_utils
43
44GTEST_FILTER_FLAG = '--gtest_filter'
45GTEST_LIST_TESTS_FLAG = '--gtest_list_tests'
46GTEST_OUTPUT_FLAG = '--gtest_output'
47GTEST_DEFAULT_OUTPUT_FILE = 'test_detail.xml'
48GTEST_PROGRAM_NAME = 'gtest_xml_output_unittest_'
49
50# The flag indicating stacktraces are not supported
51NO_STACKTRACE_SUPPORT_FLAG = '--no_stacktrace_support'
52
53# The environment variables for test sharding.
54TOTAL_SHARDS_ENV_VAR = 'GTEST_TOTAL_SHARDS'
55SHARD_INDEX_ENV_VAR = 'GTEST_SHARD_INDEX'
56SHARD_STATUS_FILE_ENV_VAR = 'GTEST_SHARD_STATUS_FILE'
57
58SUPPORTS_STACK_TRACES = NO_STACKTRACE_SUPPORT_FLAG not in sys.argv
59
60if SUPPORTS_STACK_TRACES:
61  STACK_TRACE_TEMPLATE = '\nStack trace:\n*'
62  STACK_TRACE_ENTITY_TEMPLATE = ''
63else:
64  STACK_TRACE_TEMPLATE = '\n'
65  STACK_TRACE_ENTITY_TEMPLATE = '
'
66  # unittest.main() can't handle unknown flags
67  sys.argv.remove(NO_STACKTRACE_SUPPORT_FLAG)
68
69EXPECTED_NON_EMPTY_XML = """<?xml version="1.0" encoding="UTF-8"?>
70<testsuites tests="26" failures="5" disabled="2" errors="0" time="*" timestamp="*" name="AllTests" ad_hoc_property="42">
71  <testsuite name="SuccessfulTest" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
72    <testcase name="Succeeds" file="gtest_xml_output_unittest_.cc" line="53" status="run" result="completed" time="*" timestamp="*" classname="SuccessfulTest"/>
73  </testsuite>
74  <testsuite name="FailedTest" tests="1" failures="1" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
75    <testcase name="Fails" file="gtest_xml_output_unittest_.cc" line="61" status="run" result="completed" time="*" timestamp="*" classname="FailedTest">
76      <failure message="gtest_xml_output_unittest_.cc:*&#x0A;Expected equality of these values:&#x0A;  1&#x0A;  2%(stack_entity)s" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
77Expected equality of these values:
78  1
79  2%(stack)s]]></failure>
80    </testcase>
81  </testsuite>
82  <testsuite name="MixedResultTest" tests="3" failures="1" disabled="1" skipped="0" errors="0" time="*" timestamp="*">
83    <testcase name="Succeeds" file="gtest_xml_output_unittest_.cc" line="88" status="run" result="completed" time="*" timestamp="*" classname="MixedResultTest"/>
84    <testcase name="Fails" file="gtest_xml_output_unittest_.cc" line="93" status="run" result="completed" time="*" timestamp="*" classname="MixedResultTest">
85      <failure message="gtest_xml_output_unittest_.cc:*&#x0A;Expected equality of these values:&#x0A;  1&#x0A;  2%(stack_entity)s" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
86Expected equality of these values:
87  1
88  2%(stack)s]]></failure>
89      <failure message="gtest_xml_output_unittest_.cc:*&#x0A;Expected equality of these values:&#x0A;  2&#x0A;  3%(stack_entity)s" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
90Expected equality of these values:
91  2
92  3%(stack)s]]></failure>
93    </testcase>
94    <testcase name="DISABLED_test" file="gtest_xml_output_unittest_.cc" line="98" status="notrun" result="suppressed" time="*" timestamp="*" classname="MixedResultTest"/>
95  </testsuite>
96  <testsuite name="XmlQuotingTest" tests="1" failures="1" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
97    <testcase name="OutputsCData" file="gtest_xml_output_unittest_.cc" line="102" status="run" result="completed" time="*" timestamp="*" classname="XmlQuotingTest">
98      <failure message="gtest_xml_output_unittest_.cc:*&#x0A;Failed&#x0A;XML output: &lt;?xml encoding=&quot;utf-8&quot;&gt;&lt;top&gt;&lt;![CDATA[cdata text]]&gt;&lt;/top&gt;%(stack_entity)s" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
99Failed
100XML output: <?xml encoding="utf-8"><top><![CDATA[cdata text]]>]]&gt;<![CDATA[</top>%(stack)s]]></failure>
101    </testcase>
102  </testsuite>
103  <testsuite name="InvalidCharactersTest" tests="1" failures="1" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
104    <testcase name="InvalidCharactersInMessage" file="gtest_xml_output_unittest_.cc" line="109" status="run" result="completed" time="*" timestamp="*" classname="InvalidCharactersTest">
105      <failure message="gtest_xml_output_unittest_.cc:*&#x0A;Failed&#x0A;Invalid characters in brackets []%(stack_entity)s" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
106Failed
107Invalid characters in brackets []%(stack)s]]></failure>
108    </testcase>
109  </testsuite>
110  <testsuite name="DisabledTest" tests="1" failures="0" disabled="1" skipped="0" errors="0" time="*" timestamp="*">
111    <testcase name="DISABLED_test_not_run" file="gtest_xml_output_unittest_.cc" line="68" status="notrun" result="suppressed" time="*" timestamp="*" classname="DisabledTest"/>
112  </testsuite>
113  <testsuite name="SkippedTest" tests="3" failures="1" disabled="0" skipped="2" errors="0" time="*" timestamp="*">
114    <testcase name="Skipped" status="run" file="gtest_xml_output_unittest_.cc" line="75" result="skipped" time="*" timestamp="*" classname="SkippedTest">
115      <skipped message="gtest_xml_output_unittest_.cc:*&#x0A;%(stack_entity)s"><![CDATA[gtest_xml_output_unittest_.cc:*
116%(stack)s]]></skipped>
117    </testcase>
118    <testcase name="SkippedWithMessage" file="gtest_xml_output_unittest_.cc" line="79" status="run" result="skipped" time="*" timestamp="*" classname="SkippedTest">
119      <skipped message="gtest_xml_output_unittest_.cc:*&#x0A;It is good practice to tell why you skip a test.%(stack_entity)s"><![CDATA[gtest_xml_output_unittest_.cc:*
120It is good practice to tell why you skip a test.%(stack)s]]></skipped>
121    </testcase>
122    <testcase name="SkippedAfterFailure" file="gtest_xml_output_unittest_.cc" line="83" status="run" result="completed" time="*" timestamp="*" classname="SkippedTest">
123      <failure message="gtest_xml_output_unittest_.cc:*&#x0A;Expected equality of these values:&#x0A;  1&#x0A;  2%(stack_entity)s" type=""><![CDATA[gtest_xml_output_unittest_.cc:*
124Expected equality of these values:
125  1
126  2%(stack)s]]></failure>
127      <skipped message="gtest_xml_output_unittest_.cc:*&#x0A;It is good practice to tell why you skip a test.%(stack_entity)s"><![CDATA[gtest_xml_output_unittest_.cc:*
128It is good practice to tell why you skip a test.%(stack)s]]></skipped>
129    </testcase>
130
131  </testsuite>
132  <testsuite name="PropertyRecordingTest" tests="4" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*" SetUpTestSuite="yes" TearDownTestSuite="aye">
133    <testcase name="OneProperty" file="gtest_xml_output_unittest_.cc" line="121" status="run" result="completed" time="*" timestamp="*" classname="PropertyRecordingTest">
134      <properties>
135        <property name="key_1" value="1"/>
136      </properties>
137    </testcase>
138    <testcase name="IntValuedProperty" file="gtest_xml_output_unittest_.cc" line="125" status="run" result="completed" time="*" timestamp="*" classname="PropertyRecordingTest">
139      <properties>
140        <property name="key_int" value="1"/>
141      </properties>
142    </testcase>
143    <testcase name="ThreeProperties" file="gtest_xml_output_unittest_.cc" line="129" status="run" result="completed" time="*" timestamp="*" classname="PropertyRecordingTest">
144      <properties>
145        <property name="key_1" value="1"/>
146        <property name="key_2" value="2"/>
147        <property name="key_3" value="3"/>
148      </properties>
149    </testcase>
150    <testcase name="TwoValuesForOneKeyUsesLastValue" file="gtest_xml_output_unittest_.cc" line="135" status="run" result="completed" time="*" timestamp="*" classname="PropertyRecordingTest">
151      <properties>
152        <property name="key_1" value="2"/>
153      </properties>
154    </testcase>
155  </testsuite>
156  <testsuite name="NoFixtureTest" tests="3" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
157     <testcase name="RecordProperty" file="gtest_xml_output_unittest_.cc" line="140" status="run" result="completed" time="*" timestamp="*" classname="NoFixtureTest">
158       <properties>
159         <property name="key" value="1"/>
160       </properties>
161     </testcase>
162     <testcase name="ExternalUtilityThatCallsRecordIntValuedProperty" file="gtest_xml_output_unittest_.cc" line="153" status="run" result="completed" time="*" timestamp="*" classname="NoFixtureTest">
163       <properties>
164         <property name="key_for_utility_int" value="1"/>
165       </properties>
166     </testcase>
167     <testcase name="ExternalUtilityThatCallsRecordStringValuedProperty" file="gtest_xml_output_unittest_.cc" line="157" status="run" result="completed" time="*" timestamp="*" classname="NoFixtureTest">
168       <properties>
169         <property name="key_for_utility_string" value="1"/>
170       </properties>
171     </testcase>
172  </testsuite>
173  <testsuite name="Single/ValueParamTest" tests="4" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
174    <testcase name="HasValueParamAttribute/0" file="gtest_xml_output_unittest_.cc" line="164" value_param="33" status="run" result="completed" time="*" timestamp="*" classname="Single/ValueParamTest" />
175    <testcase name="HasValueParamAttribute/1" file="gtest_xml_output_unittest_.cc" line="164" value_param="42" status="run" result="completed" time="*" timestamp="*" classname="Single/ValueParamTest" />
176    <testcase name="AnotherTestThatHasValueParamAttribute/0" file="gtest_xml_output_unittest_.cc" line="165" value_param="33" status="run" result="completed" time="*" timestamp="*" classname="Single/ValueParamTest" />
177    <testcase name="AnotherTestThatHasValueParamAttribute/1" file="gtest_xml_output_unittest_.cc" line="165" value_param="42" status="run" result="completed" time="*" timestamp="*" classname="Single/ValueParamTest" />
178  </testsuite>
179  <testsuite name="TypedTest/0" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
180    <testcase name="HasTypeParamAttribute" file="gtest_xml_output_unittest_.cc" line="173" type_param="*" status="run" result="completed" time="*" timestamp="*" classname="TypedTest/0" />
181  </testsuite>
182  <testsuite name="TypedTest/1" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
183    <testcase name="HasTypeParamAttribute" file="gtest_xml_output_unittest_.cc" line="173" type_param="*" status="run" result="completed" time="*" timestamp="*" classname="TypedTest/1" />
184  </testsuite>
185  <testsuite name="Single/TypeParameterizedTestSuite/0" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
186    <testcase name="HasTypeParamAttribute" file="gtest_xml_output_unittest_.cc" line="180" type_param="*" status="run" result="completed" time="*" timestamp="*" classname="Single/TypeParameterizedTestSuite/0" />
187  </testsuite>
188  <testsuite name="Single/TypeParameterizedTestSuite/1" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
189    <testcase name="HasTypeParamAttribute" file="gtest_xml_output_unittest_.cc" line="180" type_param="*" status="run" result="completed" time="*" timestamp="*" classname="Single/TypeParameterizedTestSuite/1" />
190  </testsuite>
191</testsuites>""" % {
192    'stack': STACK_TRACE_TEMPLATE,
193    'stack_entity': STACK_TRACE_ENTITY_TEMPLATE,
194}
195
196EXPECTED_FILTERED_TEST_XML = """<?xml version="1.0" encoding="UTF-8"?>
197<testsuites tests="1" failures="0" disabled="0" errors="0" time="*"
198            timestamp="*" name="AllTests" ad_hoc_property="42">
199  <testsuite name="SuccessfulTest" tests="1" failures="0" disabled="0" skipped="0"
200             errors="0" time="*" timestamp="*">
201    <testcase name="Succeeds" file="gtest_xml_output_unittest_.cc" line="53" status="run" result="completed" time="*" timestamp="*" classname="SuccessfulTest"/>
202  </testsuite>
203</testsuites>"""
204
205EXPECTED_SHARDED_TEST_XML = """<?xml version="1.0" encoding="UTF-8"?>
206<testsuites tests="3" failures="0" disabled="0" errors="0" time="*" timestamp="*" name="AllTests" ad_hoc_property="42">
207  <testsuite name="SuccessfulTest" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
208    <testcase name="Succeeds" file="gtest_xml_output_unittest_.cc" line="53" status="run" result="completed" time="*" timestamp="*" classname="SuccessfulTest"/>
209  </testsuite>
210  <testsuite name="PropertyRecordingTest" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*" SetUpTestSuite="yes" TearDownTestSuite="aye">
211    <testcase name="IntValuedProperty" file="gtest_xml_output_unittest_.cc" line="125" status="run" result="completed" time="*" timestamp="*" classname="PropertyRecordingTest">
212      <properties>
213        <property name="key_int" value="1"/>
214      </properties>
215    </testcase>
216  </testsuite>
217  <testsuite name="Single/ValueParamTest" tests="1" failures="0" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
218    <testcase name="HasValueParamAttribute/0" file="gtest_xml_output_unittest_.cc" line="164" value_param="33" status="run" result="completed" time="*" timestamp="*" classname="Single/ValueParamTest" />
219  </testsuite>
220</testsuites>"""
221
222EXPECTED_NO_TEST_XML = """<?xml version="1.0" encoding="UTF-8"?>
223<testsuites tests="0" failures="0" disabled="0" errors="0" time="*"
224            timestamp="*" name="AllTests">
225  <testsuite name="NonTestSuiteFailure" tests="1" failures="1" disabled="0" skipped="0" errors="0" time="*" timestamp="*">
226    <testcase name="" status="run" result="completed" time="*" timestamp="*" classname="">
227      <failure message="gtest_no_test_unittest.cc:*&#x0A;Expected equality of these values:&#x0A;  1&#x0A;  2%(stack_entity)s" type=""><![CDATA[gtest_no_test_unittest.cc:*
228Expected equality of these values:
229  1
230  2%(stack)s]]></failure>
231    </testcase>
232  </testsuite>
233</testsuites>""" % {
234    'stack': STACK_TRACE_TEMPLATE,
235    'stack_entity': STACK_TRACE_ENTITY_TEMPLATE,
236}
237
238GTEST_PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath(GTEST_PROGRAM_NAME)
239
240SUPPORTS_TYPED_TESTS = (
241    'TypedTest'
242    in gtest_test_utils.Subprocess(
243        [GTEST_PROGRAM_PATH, GTEST_LIST_TESTS_FLAG], capture_stderr=False
244    ).output
245)
246
247
248class GTestXMLOutputUnitTest(gtest_xml_test_utils.GTestXMLTestCase):
249  """Unit test for Google Test's XML output functionality."""
250
251  # This test currently breaks on platforms that do not support typed and
252  # type-parameterized tests, so we don't run it under them.
253  if SUPPORTS_TYPED_TESTS:
254
255    def testNonEmptyXmlOutput(self):
256      """Generates non-empty XML and verifies it matches the expected output.
257
258      Runs a test program that generates a non-empty XML output, and
259      tests that the XML output is expected.
260      """
261      self._TestXmlOutput(GTEST_PROGRAM_NAME, EXPECTED_NON_EMPTY_XML, 1)
262
263  def testNoTestXmlOutput(self):
264    """Verifies XML output for a Google Test binary without actual tests.
265
266    Runs a test program that generates an XML output for a binary without tests,
267    and tests that the XML output is expected.
268    """
269
270    self._TestXmlOutput('gtest_no_test_unittest', EXPECTED_NO_TEST_XML, 0)
271
272  def testTimestampValue(self):
273    """Checks whether the timestamp attribute in the XML output is valid.
274
275    Runs a test program that generates an empty XML output, and checks if
276    the timestamp attribute in the testsuites tag is valid.
277    """
278    actual = self._GetXmlOutput('gtest_no_test_unittest', [], {}, 0)
279    date_time_str = actual.documentElement.getAttributeNode('timestamp').value
280    # datetime.strptime() is only available in Python 2.5+ so we have to
281    # parse the expected datetime manually.
282    match = re.match(r'(\d+)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)', date_time_str)
283    self.assertTrue(
284        re.match, 'XML datettime string %s has incorrect format' % date_time_str
285    )
286    date_time_from_xml = datetime.datetime(
287        year=int(match.group(1)),
288        month=int(match.group(2)),
289        day=int(match.group(3)),
290        hour=int(match.group(4)),
291        minute=int(match.group(5)),
292        second=int(match.group(6)),
293    )
294
295    time_delta = abs(datetime.datetime.now() - date_time_from_xml)
296    # timestamp value should be near the current local time
297    self.assertLess(time_delta, datetime.timedelta(seconds=600))
298    actual.unlink()
299
300  def testDefaultOutputFile(self):
301    """Tests XML file with default name is created when name is not specified.
302
303    Confirms that Google Test produces an XML output file with the expected
304    default name if no name is explicitly specified.
305    """
306    output_file = os.path.join(
307        gtest_test_utils.GetTempDir(), GTEST_DEFAULT_OUTPUT_FILE
308    )
309    gtest_prog_path = gtest_test_utils.GetTestExecutablePath(
310        'gtest_no_test_unittest'
311    )
312    try:
313      os.remove(output_file)
314    except OSError:
315      e = sys.exc_info()[1]
316      if e.errno != errno.ENOENT:
317        raise
318
319    p = gtest_test_utils.Subprocess(
320        [gtest_prog_path, '%s=xml' % GTEST_OUTPUT_FLAG],
321        working_dir=gtest_test_utils.GetTempDir(),
322    )
323    self.assertTrue(p.exited)
324    self.assertEqual(0, p.exit_code)
325    self.assertTrue(os.path.isfile(output_file))
326
327  def testSuppressedXmlOutput(self):
328    """Verifies XML output is suppressed if default listener is shut down.
329
330    Tests that no XML file is generated if the default XML listener is
331    shut down before RUN_ALL_TESTS is invoked.
332    """
333
334    xml_path = os.path.join(
335        gtest_test_utils.GetTempDir(), GTEST_PROGRAM_NAME + 'out.xml'
336    )
337    if os.path.isfile(xml_path):
338      os.remove(xml_path)
339
340    command = [
341        GTEST_PROGRAM_PATH,
342        '%s=xml:%s' % (GTEST_OUTPUT_FLAG, xml_path),
343        '--shut_down_xml',
344    ]
345    p = gtest_test_utils.Subprocess(command)
346    if p.terminated_by_signal:
347      # p.signal is available only if p.terminated_by_signal is True.
348      self.assertFalse(
349          p.terminated_by_signal,
350          '%s was killed by signal %d' % (GTEST_PROGRAM_NAME, p.signal),
351      )
352    else:
353      self.assertTrue(p.exited)
354      self.assertEqual(
355          1,
356          p.exit_code,
357          "'%s' exited with code %s, which doesn't match "
358          'the expected exit code %s.' % (command, p.exit_code, 1),
359      )
360
361    self.assertFalse(os.path.isfile(xml_path))
362
363  def testFilteredTestXmlOutput(self):
364    """Verifies XML output when a filter is applied.
365
366    Runs a test program that executes only some tests and verifies that
367    non-selected tests do not show up in the XML output.
368    """
369
370    self._TestXmlOutput(
371        GTEST_PROGRAM_NAME,
372        EXPECTED_FILTERED_TEST_XML,
373        0,
374        extra_args=['%s=SuccessfulTest.*' % GTEST_FILTER_FLAG],
375    )
376
377  def testShardedTestXmlOutput(self):
378    """Verifies XML output when run using multiple shards.
379
380    Runs a test program that executes only one shard and verifies that tests
381    from other shards do not show up in the XML output.
382    """
383
384    self._TestXmlOutput(
385        GTEST_PROGRAM_NAME,
386        EXPECTED_SHARDED_TEST_XML,
387        0,
388        extra_env={SHARD_INDEX_ENV_VAR: '0', TOTAL_SHARDS_ENV_VAR: '10'},
389    )
390
391  def _GetXmlOutput(
392      self, gtest_prog_name, extra_args, extra_env, expected_exit_code
393  ):
394    """Returns the XML output generated by running the program gtest_prog_name.
395
396    Furthermore, the program's exit code must be expected_exit_code.
397
398    Args:
399      gtest_prog_name: Program to run.
400      extra_args: Optional arguments to pass to program.
401      extra_env: Optional environment variables to set.
402      expected_exit_code: Expected exit code from running gtest_prog_name.
403    """
404    xml_path = os.path.join(
405        gtest_test_utils.GetTempDir(), gtest_prog_name + 'out.xml'
406    )
407    gtest_prog_path = gtest_test_utils.GetTestExecutablePath(gtest_prog_name)
408
409    command = [
410        gtest_prog_path,
411        '%s=xml:%s' % (GTEST_OUTPUT_FLAG, xml_path),
412    ] + extra_args
413    environ_copy = os.environ.copy()
414    if extra_env:
415      environ_copy.update(extra_env)
416    p = gtest_test_utils.Subprocess(command, env=environ_copy)
417
418    if p.terminated_by_signal:
419      self.assertTrue(
420          False, '%s was killed by signal %d' % (gtest_prog_name, p.signal)
421      )
422    else:
423      self.assertTrue(p.exited)
424      self.assertEqual(
425          expected_exit_code,
426          p.exit_code,
427          "'%s' exited with code %s, which doesn't match "
428          'the expected exit code %s.'
429          % (command, p.exit_code, expected_exit_code),
430      )
431    actual = minidom.parse(xml_path)
432    return actual
433
434  def _TestXmlOutput(
435      self,
436      gtest_prog_name,
437      expected_xml,
438      expected_exit_code,
439      extra_args=None,
440      extra_env=None,
441  ):
442    """Asserts that the XML document matches.
443
444    Asserts that the XML document generated by running the program
445    gtest_prog_name matches expected_xml, a string containing another
446    XML document.  Furthermore, the program's exit code must be
447    expected_exit_code.
448
449    Args:
450      gtest_prog_name: Program to run.
451      expected_xml: Path to XML document to match.
452      expected_exit_code: Expected exit code from running gtest_prog_name.
453      extra_args: Optional arguments to pass to program.
454      extra_env: Optional environment variables to set.
455    """
456
457    actual = self._GetXmlOutput(
458        gtest_prog_name, extra_args or [], extra_env or {}, expected_exit_code
459    )
460    expected = minidom.parseString(expected_xml)
461    self.NormalizeXml(actual.documentElement)
462    self.AssertEquivalentNodes(expected.documentElement, actual.documentElement)
463    expected.unlink()
464    actual.unlink()
465
466
467if __name__ == '__main__':
468  os.environ['GTEST_STACK_TRACE_DEPTH'] = '1'
469  gtest_test_utils.Main()
470