1# Copyright (c) 2013 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Module containing base test results classes."""
6
7import threading
8
9
10class ResultType(object):
11  """Class enumerating test types."""
12  PASS = 'PASS'
13  SKIP = 'SKIP'
14  FAIL = 'FAIL'
15  CRASH = 'CRASH'
16  TIMEOUT = 'TIMEOUT'
17  UNKNOWN = 'UNKNOWN'
18
19  @staticmethod
20  def GetTypes():
21    """Get a list of all test types."""
22    return [ResultType.PASS, ResultType.SKIP, ResultType.FAIL,
23            ResultType.CRASH, ResultType.TIMEOUT, ResultType.UNKNOWN]
24
25
26class BaseTestResult(object):
27  """Base class for a single test result."""
28
29  def __init__(self, name, test_type, duration=0, log=''):
30    """Construct a BaseTestResult.
31
32    Args:
33      name: Name of the test which defines uniqueness.
34      test_type: Type of the test result as defined in ResultType.
35      duration: Time it took for the test to run in milliseconds.
36      log: An optional string listing any errors.
37    """
38    assert name
39    assert test_type in ResultType.GetTypes()
40    self._name = name
41    self._test_type = test_type
42    self._duration = duration
43    self._log = log
44
45  def __str__(self):
46    return self._name
47
48  def __repr__(self):
49    return self._name
50
51  def __cmp__(self, other):
52    # pylint: disable=W0212
53    return cmp(self._name, other._name)
54
55  def __hash__(self):
56    return hash(self._name)
57
58  def SetName(self, name):
59    """Set the test name.
60
61    Because we're putting this into a set, this should only be used if moving
62    this test result into another set.
63    """
64    self._name = name
65
66  def GetName(self):
67    """Get the test name."""
68    return self._name
69
70  def SetType(self, test_type):
71    """Set the test result type."""
72    assert test_type in ResultType.GetTypes()
73    self._test_type = test_type
74
75  def GetType(self):
76    """Get the test result type."""
77    return self._test_type
78
79  def GetDuration(self):
80    """Get the test duration."""
81    return self._duration
82
83  def SetLog(self, log):
84    """Set the test log."""
85    self._log = log
86
87  def GetLog(self):
88    """Get the test log."""
89    return self._log
90
91
92class TestRunResults(object):
93  """Set of results for a test run."""
94
95  def __init__(self):
96    self._results = set()
97    self._results_lock = threading.RLock()
98
99  def GetLogs(self):
100    """Get the string representation of all test logs."""
101    with self._results_lock:
102      s = []
103      for test_type in ResultType.GetTypes():
104        if test_type != ResultType.PASS:
105          for t in sorted(self._GetType(test_type)):
106            log = t.GetLog()
107            if log:
108              s.append('[%s] %s:' % (test_type, t))
109              s.append(log)
110      return '\n'.join(s)
111
112  def GetGtestForm(self):
113    """Get the gtest string representation of this object."""
114    with self._results_lock:
115      s = []
116      plural = lambda n, s, p: '%d %s' % (n, p if n != 1 else s)
117      tests = lambda n: plural(n, 'test', 'tests')
118
119      s.append('[==========] %s ran.' % (tests(len(self.GetAll()))))
120      s.append('[  PASSED  ] %s.' % (tests(len(self.GetPass()))))
121
122      skipped = self.GetSkip()
123      if skipped:
124        s.append('[  SKIPPED ] Skipped %s, listed below:' % tests(len(skipped)))
125        for t in sorted(skipped):
126          s.append('[  SKIPPED ] %s' % str(t))
127
128      all_failures = self.GetFail().union(self.GetCrash(), self.GetTimeout(),
129          self.GetUnknown())
130      if all_failures:
131        s.append('[  FAILED  ] %s, listed below:' % tests(len(all_failures)))
132        for t in sorted(self.GetFail()):
133          s.append('[  FAILED  ] %s' % str(t))
134        for t in sorted(self.GetCrash()):
135          s.append('[  FAILED  ] %s (CRASHED)' % str(t))
136        for t in sorted(self.GetTimeout()):
137          s.append('[  FAILED  ] %s (TIMEOUT)' % str(t))
138        for t in sorted(self.GetUnknown()):
139          s.append('[  FAILED  ] %s (UNKNOWN)' % str(t))
140        s.append('')
141        s.append(plural(len(all_failures), 'FAILED TEST', 'FAILED TESTS'))
142      return '\n'.join(s)
143
144  def GetShortForm(self):
145    """Get the short string representation of this object."""
146    with self._results_lock:
147      s = []
148      s.append('ALL: %d' % len(self._results))
149      for test_type in ResultType.GetTypes():
150        s.append('%s: %d' % (test_type, len(self._GetType(test_type))))
151      return ''.join([x.ljust(15) for x in s])
152
153  def __str__(self):
154    return self.GetGtestForm()
155
156  def AddResult(self, result):
157    """Add |result| to the set.
158
159    Args:
160      result: An instance of BaseTestResult.
161    """
162    assert isinstance(result, BaseTestResult)
163    with self._results_lock:
164      self._results.add(result)
165
166  def AddResults(self, results):
167    """Add |results| to the set.
168
169    Args:
170      results: An iterable of BaseTestResult objects.
171    """
172    with self._results_lock:
173      for t in results:
174        self.AddResult(t)
175
176  def AddTestRunResults(self, results):
177    """Add the set of test results from |results|.
178
179    Args:
180      results: An instance of TestRunResults.
181    """
182    assert isinstance(results, TestRunResults)
183    with self._results_lock:
184      # pylint: disable=W0212
185      self._results.update(results._results)
186
187  def GetAll(self):
188    """Get the set of all test results."""
189    with self._results_lock:
190      return self._results.copy()
191
192  def _GetType(self, test_type):
193    """Get the set of test results with the given test type."""
194    with self._results_lock:
195      return set(t for t in self._results if t.GetType() == test_type)
196
197  def GetPass(self):
198    """Get the set of all passed test results."""
199    return self._GetType(ResultType.PASS)
200
201  def GetSkip(self):
202    """Get the set of all skipped test results."""
203    return self._GetType(ResultType.SKIP)
204
205  def GetFail(self):
206    """Get the set of all failed test results."""
207    return self._GetType(ResultType.FAIL)
208
209  def GetCrash(self):
210    """Get the set of all crashed test results."""
211    return self._GetType(ResultType.CRASH)
212
213  def GetTimeout(self):
214    """Get the set of all timed out test results."""
215    return self._GetType(ResultType.TIMEOUT)
216
217  def GetUnknown(self):
218    """Get the set of all unknown test results."""
219    return self._GetType(ResultType.UNKNOWN)
220
221  def GetNotPass(self):
222    """Get the set of all non-passed test results."""
223    return self.GetAll() - self.GetPass()
224
225  def DidRunPass(self):
226    """Return whether the test run was successful."""
227    return not self.GetNotPass() - self.GetSkip()
228
229