1# -*- coding: utf-8 -*-
2# Copyright 2010-2018, Google Inc.
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9#     * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11#     * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15#     * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31"""Converting gtest result xml to python object.
32
33The gtest can output test result as xml file. Following class can parse it and
34store as python object.
35"""
36
37__author__ = "nona"
38
39import io
40import logging
41from xml.etree import ElementTree
42
43
44class Failure(object):
45  """Corresponding to failure element in test output xml."""
46
47  def __init__(self, message, contents):
48    self.message = message
49    self.contents = contents
50
51  @classmethod
52  def CreateFromXMLElement(cls, element):
53    return cls(element.get('message'), element.text)
54
55
56class TestCase(object):
57  """Corresponding to testcase element in test output xml."""
58
59  def __init__(self, name, status, time, classname, failures):
60    self.name = name
61    self.status = status
62    self.time = time
63    self.classname = classname
64    self.failures = failures
65
66  @classmethod
67  def CreateFromXMLElement(cls, element):
68    failures = [Failure.CreateFromXMLElement(failure) for failure in element]
69    return cls(element.get('name'), element.get('status'),
70               element.get('time'), element.get('classname'), failures)
71
72
73class TestSuite(object):
74  """Corresponding to testsuite element in test output xml."""
75
76  def __init__(self, name, total, fail_num, disabled_num, error_num, time,
77               testcases):
78    self.name = name
79    self.total = int(total)
80    self.fail_num = int(fail_num)
81    self.disabled_num = int(disabled_num)
82    self.error_num = int(error_num)
83    self.time = time
84    self.testcases = testcases
85
86  def GetErrorSummary(self):
87    """Returns summarized error report text."""
88    if self.fail_num == 0:
89      return ''
90    output = io.StringIO()
91    for testcase in self.testcases:
92      if not testcase.failures:
93        continue
94      print('%s.%s:' % (self.name, testcase.name), file=output)
95      for failure in testcase.failures:
96        print(failure.contents.encode('utf-8'), file=output)
97    return output.getvalue()
98
99  @classmethod
100  def CreateFromXMLElement(cls, element):
101    testcases = [TestCase.CreateFromXMLElement(testcase) for
102                 testcase in element]
103    return cls(element.get('name'), element.get('tests'),
104               element.get('failures'), element.get('disabled'),
105               element.get('errors'), element.get('time'), testcases)
106
107
108class TestSuites(object):
109  """Corresponding to testsuites element in output xml."""
110
111  def __init__(self, name, total, fail_num, disabled_num, error_num, time,
112               timestamp, testsuites):
113    self.name = name
114    self.total = int(total)
115    self.fail_num = int(fail_num)
116    self.disabled_num = int(disabled_num)
117    self.error_num = int(error_num)
118    self.timestamp = timestamp
119    self.time = time
120    self.testsuites = testsuites
121
122  def GetErrorSummary(self):
123    """Returns summarized unit test errors."""
124    return '\n'.join(suite.GetErrorSummary() for suite in self.testsuites if
125                     suite.fail_num != 0)
126
127  @classmethod
128  def CreateFromXMLElement(cls, element):
129    testsuites = [TestSuite.CreateFromXMLElement(testsuite) for
130                  testsuite in element]
131    return cls(element.get('name'), element.get('tests'),
132               element.get('failures'), element.get('disabled'),
133               element.get('errors'), element.get('time'),
134               element.get('timestamp'), testsuites)
135
136
137def GetFromXMLFile(xml_fname):
138  """Create summary object from specified xml file."""
139  try:
140    tree = ElementTree.parse(xml_fname)
141  except SyntaxError as reason:
142    logging.critical('XML Parse Failed: %s', reason)
143    return None
144  return TestSuites.CreateFromXMLElement(tree.getroot())
145