xref: /aosp_15_r20/external/pdfium/testing/tools/safetynet_conclusions.py (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1*3ac0a46fSAndroid Build Coastguard Worker# Copyright 2017 The PDFium Authors
2*3ac0a46fSAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
3*3ac0a46fSAndroid Build Coastguard Worker# found in the LICENSE file.
4*3ac0a46fSAndroid Build Coastguard Worker"""Classes that draw conclusions out of a comparison and represent them."""
5*3ac0a46fSAndroid Build Coastguard Worker
6*3ac0a46fSAndroid Build Coastguard Workerfrom collections import Counter
7*3ac0a46fSAndroid Build Coastguard Worker
8*3ac0a46fSAndroid Build Coastguard WorkerFORMAT_RED = '\033[01;31m{0}\033[00m'
9*3ac0a46fSAndroid Build Coastguard WorkerFORMAT_GREEN = '\033[01;32m{0}\033[00m'
10*3ac0a46fSAndroid Build Coastguard WorkerFORMAT_MAGENTA = '\033[01;35m{0}\033[00m'
11*3ac0a46fSAndroid Build Coastguard WorkerFORMAT_CYAN = '\033[01;36m{0}\033[00m'
12*3ac0a46fSAndroid Build Coastguard WorkerFORMAT_NORMAL = '{0}'
13*3ac0a46fSAndroid Build Coastguard Worker
14*3ac0a46fSAndroid Build Coastguard WorkerRATING_FAILURE = 'failure'
15*3ac0a46fSAndroid Build Coastguard WorkerRATING_REGRESSION = 'regression'
16*3ac0a46fSAndroid Build Coastguard WorkerRATING_IMPROVEMENT = 'improvement'
17*3ac0a46fSAndroid Build Coastguard WorkerRATING_NO_CHANGE = 'no_change'
18*3ac0a46fSAndroid Build Coastguard WorkerRATING_SMALL_CHANGE = 'small_change'
19*3ac0a46fSAndroid Build Coastguard Worker
20*3ac0a46fSAndroid Build Coastguard WorkerRATINGS = [
21*3ac0a46fSAndroid Build Coastguard Worker    RATING_FAILURE, RATING_REGRESSION, RATING_IMPROVEMENT, RATING_NO_CHANGE,
22*3ac0a46fSAndroid Build Coastguard Worker    RATING_SMALL_CHANGE
23*3ac0a46fSAndroid Build Coastguard Worker]
24*3ac0a46fSAndroid Build Coastguard Worker
25*3ac0a46fSAndroid Build Coastguard WorkerRATING_TO_COLOR = {
26*3ac0a46fSAndroid Build Coastguard Worker    RATING_FAILURE: FORMAT_MAGENTA,
27*3ac0a46fSAndroid Build Coastguard Worker    RATING_REGRESSION: FORMAT_RED,
28*3ac0a46fSAndroid Build Coastguard Worker    RATING_IMPROVEMENT: FORMAT_CYAN,
29*3ac0a46fSAndroid Build Coastguard Worker    RATING_NO_CHANGE: FORMAT_GREEN,
30*3ac0a46fSAndroid Build Coastguard Worker    RATING_SMALL_CHANGE: FORMAT_NORMAL,
31*3ac0a46fSAndroid Build Coastguard Worker}
32*3ac0a46fSAndroid Build Coastguard Worker
33*3ac0a46fSAndroid Build Coastguard Worker
34*3ac0a46fSAndroid Build Coastguard Workerclass ComparisonConclusions:
35*3ac0a46fSAndroid Build Coastguard Worker  """All conclusions drawn from a comparison.
36*3ac0a46fSAndroid Build Coastguard Worker
37*3ac0a46fSAndroid Build Coastguard Worker  This is initialized empty and then processes pairs of results for each test
38*3ac0a46fSAndroid Build Coastguard Worker  case, determining the rating for that case, which can be:
39*3ac0a46fSAndroid Build Coastguard Worker  "failure" if either or both runs for the case failed.
40*3ac0a46fSAndroid Build Coastguard Worker  "regression" if there is a significant increase in time for the test case.
41*3ac0a46fSAndroid Build Coastguard Worker  "improvement" if there is a significant decrease in time for the test case.
42*3ac0a46fSAndroid Build Coastguard Worker  "no_change" if the time for the test case did not change at all.
43*3ac0a46fSAndroid Build Coastguard Worker  "small_change" if the time for the test case changed but within the threshold.
44*3ac0a46fSAndroid Build Coastguard Worker  """
45*3ac0a46fSAndroid Build Coastguard Worker
46*3ac0a46fSAndroid Build Coastguard Worker  def __init__(self, threshold_significant):
47*3ac0a46fSAndroid Build Coastguard Worker    """Initializes an empty ComparisonConclusions.
48*3ac0a46fSAndroid Build Coastguard Worker
49*3ac0a46fSAndroid Build Coastguard Worker    Args:
50*3ac0a46fSAndroid Build Coastguard Worker      threshold_significant: Float with the tolerance beyond which changes in
51*3ac0a46fSAndroid Build Coastguard Worker          measurements are considered significant.
52*3ac0a46fSAndroid Build Coastguard Worker
53*3ac0a46fSAndroid Build Coastguard Worker          The change is considered as a multiplication rather than an addition
54*3ac0a46fSAndroid Build Coastguard Worker          of a fraction of the previous measurement, that is, a
55*3ac0a46fSAndroid Build Coastguard Worker          threshold_significant of 1.0 will flag test cases that became over
56*3ac0a46fSAndroid Build Coastguard Worker          100% slower (> 200% of the previous time measured) or over 100% faster
57*3ac0a46fSAndroid Build Coastguard Worker          (< 50% of the previous time measured).
58*3ac0a46fSAndroid Build Coastguard Worker
59*3ac0a46fSAndroid Build Coastguard Worker          threshold_significant 0.02 -> 98.04% to 102% is not significant
60*3ac0a46fSAndroid Build Coastguard Worker          threshold_significant 0.1 -> 90.9% to 110% is not significant
61*3ac0a46fSAndroid Build Coastguard Worker          threshold_significant 0.25 -> 80% to 125% is not significant
62*3ac0a46fSAndroid Build Coastguard Worker          threshold_significant 1 -> 50% to 200% is not significant
63*3ac0a46fSAndroid Build Coastguard Worker          threshold_significant 4 -> 20% to 500% is not significant
64*3ac0a46fSAndroid Build Coastguard Worker
65*3ac0a46fSAndroid Build Coastguard Worker    """
66*3ac0a46fSAndroid Build Coastguard Worker    self.threshold_significant = threshold_significant
67*3ac0a46fSAndroid Build Coastguard Worker    self.threshold_significant_negative = (1 / (1 + threshold_significant)) - 1
68*3ac0a46fSAndroid Build Coastguard Worker
69*3ac0a46fSAndroid Build Coastguard Worker    self.params = {'threshold': threshold_significant}
70*3ac0a46fSAndroid Build Coastguard Worker    self.summary = ComparisonSummary()
71*3ac0a46fSAndroid Build Coastguard Worker    self.case_results = {}
72*3ac0a46fSAndroid Build Coastguard Worker
73*3ac0a46fSAndroid Build Coastguard Worker  def ProcessCase(self, case_name, before, after):
74*3ac0a46fSAndroid Build Coastguard Worker    """Feeds a test case results to the ComparisonConclusions.
75*3ac0a46fSAndroid Build Coastguard Worker
76*3ac0a46fSAndroid Build Coastguard Worker    Args:
77*3ac0a46fSAndroid Build Coastguard Worker      case_name: String identifying the case.
78*3ac0a46fSAndroid Build Coastguard Worker      before: Measurement for the "before" version of the code.
79*3ac0a46fSAndroid Build Coastguard Worker      after: Measurement for the "after" version of the code.
80*3ac0a46fSAndroid Build Coastguard Worker    """
81*3ac0a46fSAndroid Build Coastguard Worker
82*3ac0a46fSAndroid Build Coastguard Worker    # Switch 0 to None to simplify the json dict output. All zeros are
83*3ac0a46fSAndroid Build Coastguard Worker    # considered failed runs, so they will be represented by "null".
84*3ac0a46fSAndroid Build Coastguard Worker    if not before:
85*3ac0a46fSAndroid Build Coastguard Worker      before = None
86*3ac0a46fSAndroid Build Coastguard Worker    if not after:
87*3ac0a46fSAndroid Build Coastguard Worker      after = None
88*3ac0a46fSAndroid Build Coastguard Worker
89*3ac0a46fSAndroid Build Coastguard Worker    if not before or not after:
90*3ac0a46fSAndroid Build Coastguard Worker      ratio = None
91*3ac0a46fSAndroid Build Coastguard Worker      rating = RATING_FAILURE
92*3ac0a46fSAndroid Build Coastguard Worker    else:
93*3ac0a46fSAndroid Build Coastguard Worker      ratio = (float(after) / before) - 1.0
94*3ac0a46fSAndroid Build Coastguard Worker      if ratio > self.threshold_significant:
95*3ac0a46fSAndroid Build Coastguard Worker        rating = RATING_REGRESSION
96*3ac0a46fSAndroid Build Coastguard Worker      elif ratio < self.threshold_significant_negative:
97*3ac0a46fSAndroid Build Coastguard Worker        rating = RATING_IMPROVEMENT
98*3ac0a46fSAndroid Build Coastguard Worker      elif ratio == 0:
99*3ac0a46fSAndroid Build Coastguard Worker        rating = RATING_NO_CHANGE
100*3ac0a46fSAndroid Build Coastguard Worker      else:
101*3ac0a46fSAndroid Build Coastguard Worker        rating = RATING_SMALL_CHANGE
102*3ac0a46fSAndroid Build Coastguard Worker
103*3ac0a46fSAndroid Build Coastguard Worker    case_result = CaseResult(case_name, before, after, ratio, rating)
104*3ac0a46fSAndroid Build Coastguard Worker
105*3ac0a46fSAndroid Build Coastguard Worker    self.summary.ProcessCaseResult(case_result)
106*3ac0a46fSAndroid Build Coastguard Worker    self.case_results[case_name] = case_result
107*3ac0a46fSAndroid Build Coastguard Worker
108*3ac0a46fSAndroid Build Coastguard Worker  def GetSummary(self):
109*3ac0a46fSAndroid Build Coastguard Worker    """Gets the ComparisonSummary with consolidated totals."""
110*3ac0a46fSAndroid Build Coastguard Worker    return self.summary
111*3ac0a46fSAndroid Build Coastguard Worker
112*3ac0a46fSAndroid Build Coastguard Worker  def GetCaseResults(self):
113*3ac0a46fSAndroid Build Coastguard Worker    """Gets a dict mapping each test case identifier to its CaseResult."""
114*3ac0a46fSAndroid Build Coastguard Worker    return self.case_results
115*3ac0a46fSAndroid Build Coastguard Worker
116*3ac0a46fSAndroid Build Coastguard Worker  def GetOutputDict(self):
117*3ac0a46fSAndroid Build Coastguard Worker    """Returns a conclusions dict with all the conclusions drawn.
118*3ac0a46fSAndroid Build Coastguard Worker
119*3ac0a46fSAndroid Build Coastguard Worker    Returns:
120*3ac0a46fSAndroid Build Coastguard Worker      A serializable dict with the format illustrated below:
121*3ac0a46fSAndroid Build Coastguard Worker      {
122*3ac0a46fSAndroid Build Coastguard Worker        "version": 1,
123*3ac0a46fSAndroid Build Coastguard Worker        "params": {
124*3ac0a46fSAndroid Build Coastguard Worker          "threshold": 0.02
125*3ac0a46fSAndroid Build Coastguard Worker        },
126*3ac0a46fSAndroid Build Coastguard Worker        "summary": {
127*3ac0a46fSAndroid Build Coastguard Worker          "total": 123,
128*3ac0a46fSAndroid Build Coastguard Worker          "failure": 1,
129*3ac0a46fSAndroid Build Coastguard Worker          "regression": 2,
130*3ac0a46fSAndroid Build Coastguard Worker          "improvement": 1,
131*3ac0a46fSAndroid Build Coastguard Worker          "no_change": 100,
132*3ac0a46fSAndroid Build Coastguard Worker          "small_change": 19
133*3ac0a46fSAndroid Build Coastguard Worker        },
134*3ac0a46fSAndroid Build Coastguard Worker        "comparison_by_case": {
135*3ac0a46fSAndroid Build Coastguard Worker          "testing/resources/new_test.pdf": {
136*3ac0a46fSAndroid Build Coastguard Worker            "before": None,
137*3ac0a46fSAndroid Build Coastguard Worker            "after": 1000,
138*3ac0a46fSAndroid Build Coastguard Worker            "ratio": None,
139*3ac0a46fSAndroid Build Coastguard Worker            "rating": "failure"
140*3ac0a46fSAndroid Build Coastguard Worker          },
141*3ac0a46fSAndroid Build Coastguard Worker          "testing/resources/test1.pdf": {
142*3ac0a46fSAndroid Build Coastguard Worker            "before": 100,
143*3ac0a46fSAndroid Build Coastguard Worker            "after": 120,
144*3ac0a46fSAndroid Build Coastguard Worker            "ratio": 0.2,
145*3ac0a46fSAndroid Build Coastguard Worker            "rating": "regression"
146*3ac0a46fSAndroid Build Coastguard Worker          },
147*3ac0a46fSAndroid Build Coastguard Worker          "testing/resources/test2.pdf": {
148*3ac0a46fSAndroid Build Coastguard Worker            "before": 100,
149*3ac0a46fSAndroid Build Coastguard Worker            "after": 2000,
150*3ac0a46fSAndroid Build Coastguard Worker            "ratio": 19.0,
151*3ac0a46fSAndroid Build Coastguard Worker            "rating": "regression"
152*3ac0a46fSAndroid Build Coastguard Worker          },
153*3ac0a46fSAndroid Build Coastguard Worker          "testing/resources/test3.pdf": {
154*3ac0a46fSAndroid Build Coastguard Worker            "before": 1000,
155*3ac0a46fSAndroid Build Coastguard Worker            "after": 1005,
156*3ac0a46fSAndroid Build Coastguard Worker            "ratio": 0.005,
157*3ac0a46fSAndroid Build Coastguard Worker            "rating": "small_change"
158*3ac0a46fSAndroid Build Coastguard Worker          },
159*3ac0a46fSAndroid Build Coastguard Worker          "testing/resources/test4.pdf": {
160*3ac0a46fSAndroid Build Coastguard Worker            "before": 1000,
161*3ac0a46fSAndroid Build Coastguard Worker            "after": 1000,
162*3ac0a46fSAndroid Build Coastguard Worker            "ratio": 0.0,
163*3ac0a46fSAndroid Build Coastguard Worker            "rating": "no_change"
164*3ac0a46fSAndroid Build Coastguard Worker          },
165*3ac0a46fSAndroid Build Coastguard Worker          "testing/resources/test5.pdf": {
166*3ac0a46fSAndroid Build Coastguard Worker            "before": 1000,
167*3ac0a46fSAndroid Build Coastguard Worker            "after": 600,
168*3ac0a46fSAndroid Build Coastguard Worker            "ratio": -0.4,
169*3ac0a46fSAndroid Build Coastguard Worker            "rating": "improvement"
170*3ac0a46fSAndroid Build Coastguard Worker          }
171*3ac0a46fSAndroid Build Coastguard Worker        }
172*3ac0a46fSAndroid Build Coastguard Worker      }
173*3ac0a46fSAndroid Build Coastguard Worker    """
174*3ac0a46fSAndroid Build Coastguard Worker    output_dict = {}
175*3ac0a46fSAndroid Build Coastguard Worker    output_dict['version'] = 1
176*3ac0a46fSAndroid Build Coastguard Worker    output_dict['params'] = {'threshold': self.threshold_significant}
177*3ac0a46fSAndroid Build Coastguard Worker    output_dict['summary'] = self.summary.GetOutputDict()
178*3ac0a46fSAndroid Build Coastguard Worker    output_dict['comparison_by_case'] = {
179*3ac0a46fSAndroid Build Coastguard Worker        cr.case_name.decode('utf-8'): cr.GetOutputDict()
180*3ac0a46fSAndroid Build Coastguard Worker        for cr in self.GetCaseResults().values()
181*3ac0a46fSAndroid Build Coastguard Worker    }
182*3ac0a46fSAndroid Build Coastguard Worker    return output_dict
183*3ac0a46fSAndroid Build Coastguard Worker
184*3ac0a46fSAndroid Build Coastguard Worker
185*3ac0a46fSAndroid Build Coastguard Workerclass ComparisonSummary:
186*3ac0a46fSAndroid Build Coastguard Worker  """Totals computed for a comparison."""
187*3ac0a46fSAndroid Build Coastguard Worker
188*3ac0a46fSAndroid Build Coastguard Worker  def __init__(self):
189*3ac0a46fSAndroid Build Coastguard Worker    self.rating_counter = Counter()
190*3ac0a46fSAndroid Build Coastguard Worker
191*3ac0a46fSAndroid Build Coastguard Worker  def ProcessCaseResult(self, case_result):
192*3ac0a46fSAndroid Build Coastguard Worker    self.rating_counter[case_result.rating] += 1
193*3ac0a46fSAndroid Build Coastguard Worker
194*3ac0a46fSAndroid Build Coastguard Worker  def GetTotal(self):
195*3ac0a46fSAndroid Build Coastguard Worker    """Gets the number of test cases processed."""
196*3ac0a46fSAndroid Build Coastguard Worker    return sum(self.rating_counter.values())
197*3ac0a46fSAndroid Build Coastguard Worker
198*3ac0a46fSAndroid Build Coastguard Worker  def GetCount(self, rating):
199*3ac0a46fSAndroid Build Coastguard Worker    """Gets the number of test cases processed with a given rating."""
200*3ac0a46fSAndroid Build Coastguard Worker    return self.rating_counter[rating]
201*3ac0a46fSAndroid Build Coastguard Worker
202*3ac0a46fSAndroid Build Coastguard Worker  def GetOutputDict(self):
203*3ac0a46fSAndroid Build Coastguard Worker    """Returns a dict that can be serialized with all the totals."""
204*3ac0a46fSAndroid Build Coastguard Worker    result = {'total': self.GetTotal()}
205*3ac0a46fSAndroid Build Coastguard Worker    for rating in RATINGS:
206*3ac0a46fSAndroid Build Coastguard Worker      result[rating] = self.GetCount(rating)
207*3ac0a46fSAndroid Build Coastguard Worker    return result
208*3ac0a46fSAndroid Build Coastguard Worker
209*3ac0a46fSAndroid Build Coastguard Worker
210*3ac0a46fSAndroid Build Coastguard Workerclass CaseResult:
211*3ac0a46fSAndroid Build Coastguard Worker  """The conclusion for the comparison of a single test case."""
212*3ac0a46fSAndroid Build Coastguard Worker
213*3ac0a46fSAndroid Build Coastguard Worker  def __init__(self, case_name, before, after, ratio, rating):
214*3ac0a46fSAndroid Build Coastguard Worker    """Initializes an empty ComparisonConclusions.
215*3ac0a46fSAndroid Build Coastguard Worker
216*3ac0a46fSAndroid Build Coastguard Worker    Args:
217*3ac0a46fSAndroid Build Coastguard Worker      case_name: String identifying the case.
218*3ac0a46fSAndroid Build Coastguard Worker      before: Measurement for the "before" version of the code.
219*3ac0a46fSAndroid Build Coastguard Worker      after: Measurement for the "after" version of the code.
220*3ac0a46fSAndroid Build Coastguard Worker      ratio: Difference between |after| and |before| as a fraction of |before|.
221*3ac0a46fSAndroid Build Coastguard Worker      rating: Rating for this test case.
222*3ac0a46fSAndroid Build Coastguard Worker    """
223*3ac0a46fSAndroid Build Coastguard Worker    self.case_name = case_name
224*3ac0a46fSAndroid Build Coastguard Worker    self.before = before
225*3ac0a46fSAndroid Build Coastguard Worker    self.after = after
226*3ac0a46fSAndroid Build Coastguard Worker    self.ratio = ratio
227*3ac0a46fSAndroid Build Coastguard Worker    self.rating = rating
228*3ac0a46fSAndroid Build Coastguard Worker
229*3ac0a46fSAndroid Build Coastguard Worker  def GetOutputDict(self):
230*3ac0a46fSAndroid Build Coastguard Worker    """Returns a dict with the test case's conclusions."""
231*3ac0a46fSAndroid Build Coastguard Worker    return {
232*3ac0a46fSAndroid Build Coastguard Worker        'before': self.before,
233*3ac0a46fSAndroid Build Coastguard Worker        'after': self.after,
234*3ac0a46fSAndroid Build Coastguard Worker        'ratio': self.ratio,
235*3ac0a46fSAndroid Build Coastguard Worker        'rating': self.rating
236*3ac0a46fSAndroid Build Coastguard Worker    }
237*3ac0a46fSAndroid Build Coastguard Worker
238*3ac0a46fSAndroid Build Coastguard Worker
239*3ac0a46fSAndroid Build Coastguard Workerdef PrintConclusionsDictHumanReadable(conclusions_dict, colored, key=None):
240*3ac0a46fSAndroid Build Coastguard Worker  """Prints a conclusions dict in a human-readable way.
241*3ac0a46fSAndroid Build Coastguard Worker
242*3ac0a46fSAndroid Build Coastguard Worker  Args:
243*3ac0a46fSAndroid Build Coastguard Worker    conclusions_dict: Dict to print.
244*3ac0a46fSAndroid Build Coastguard Worker    colored: Whether to color the output to highlight significant changes.
245*3ac0a46fSAndroid Build Coastguard Worker    key: String with the CaseResult dictionary key to sort the cases.
246*3ac0a46fSAndroid Build Coastguard Worker  """
247*3ac0a46fSAndroid Build Coastguard Worker  # Print header
248*3ac0a46fSAndroid Build Coastguard Worker  print('=' * 80)
249*3ac0a46fSAndroid Build Coastguard Worker  print('{0:>11s} {1:>15s}  {2}'.format('% Change', 'Time after', 'Test case'))
250*3ac0a46fSAndroid Build Coastguard Worker  print('-' * 80)
251*3ac0a46fSAndroid Build Coastguard Worker
252*3ac0a46fSAndroid Build Coastguard Worker  color = FORMAT_NORMAL
253*3ac0a46fSAndroid Build Coastguard Worker
254*3ac0a46fSAndroid Build Coastguard Worker  # Print cases
255*3ac0a46fSAndroid Build Coastguard Worker  if key is not None:
256*3ac0a46fSAndroid Build Coastguard Worker    case_pairs = sorted(
257*3ac0a46fSAndroid Build Coastguard Worker        conclusions_dict['comparison_by_case'].iteritems(),
258*3ac0a46fSAndroid Build Coastguard Worker        key=lambda kv: kv[1][key])
259*3ac0a46fSAndroid Build Coastguard Worker  else:
260*3ac0a46fSAndroid Build Coastguard Worker    case_pairs = sorted(conclusions_dict['comparison_by_case'].iteritems())
261*3ac0a46fSAndroid Build Coastguard Worker
262*3ac0a46fSAndroid Build Coastguard Worker  for case_name, case_dict in case_pairs:
263*3ac0a46fSAndroid Build Coastguard Worker    if colored:
264*3ac0a46fSAndroid Build Coastguard Worker      color = RATING_TO_COLOR[case_dict['rating']]
265*3ac0a46fSAndroid Build Coastguard Worker
266*3ac0a46fSAndroid Build Coastguard Worker    if case_dict['rating'] == RATING_FAILURE:
267*3ac0a46fSAndroid Build Coastguard Worker      print(u'{} to measure time for {}'.format(
268*3ac0a46fSAndroid Build Coastguard Worker          color.format('Failed'), case_name).encode('utf-8'))
269*3ac0a46fSAndroid Build Coastguard Worker      continue
270*3ac0a46fSAndroid Build Coastguard Worker
271*3ac0a46fSAndroid Build Coastguard Worker    print(u'{0} {1:15,d}  {2}'.format(
272*3ac0a46fSAndroid Build Coastguard Worker        color.format('{:+11.4%}'.format(case_dict['ratio'])),
273*3ac0a46fSAndroid Build Coastguard Worker        case_dict['after'], case_name).encode('utf-8'))
274*3ac0a46fSAndroid Build Coastguard Worker
275*3ac0a46fSAndroid Build Coastguard Worker  # Print totals
276*3ac0a46fSAndroid Build Coastguard Worker  totals = conclusions_dict['summary']
277*3ac0a46fSAndroid Build Coastguard Worker  print('=' * 80)
278*3ac0a46fSAndroid Build Coastguard Worker  print('Test cases run: %d' % totals['total'])
279*3ac0a46fSAndroid Build Coastguard Worker
280*3ac0a46fSAndroid Build Coastguard Worker  if colored:
281*3ac0a46fSAndroid Build Coastguard Worker    color = FORMAT_MAGENTA if totals[RATING_FAILURE] else FORMAT_GREEN
282*3ac0a46fSAndroid Build Coastguard Worker  print('Failed to measure: %s' % color.format(totals[RATING_FAILURE]))
283*3ac0a46fSAndroid Build Coastguard Worker
284*3ac0a46fSAndroid Build Coastguard Worker  if colored:
285*3ac0a46fSAndroid Build Coastguard Worker    color = FORMAT_RED if totals[RATING_REGRESSION] else FORMAT_GREEN
286*3ac0a46fSAndroid Build Coastguard Worker  print('Regressions: %s' % color.format(totals[RATING_REGRESSION]))
287*3ac0a46fSAndroid Build Coastguard Worker
288*3ac0a46fSAndroid Build Coastguard Worker  if colored:
289*3ac0a46fSAndroid Build Coastguard Worker    color = FORMAT_CYAN if totals[RATING_IMPROVEMENT] else FORMAT_GREEN
290*3ac0a46fSAndroid Build Coastguard Worker  print('Improvements: %s' % color.format(totals[RATING_IMPROVEMENT]))
291