1#!/usr/bin/env python
2# Copyright 2014 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5import os
6import re
7import unittest
8
9import PRESUBMIT
10
11class MockInputApi(object):
12  def __init__(self):
13    self.re = re
14    self.os_path = os.path
15    self.files = []
16    self.is_committing = False
17
18  def AffectedFiles(self):
19    return self.files
20
21  def AffectedSourceFiles(self, fn):
22    # we'll just pretend everything is a source file for the sake of simplicity
23    return self.files
24
25  def ReadFile(self, f):
26    return f.NewContents()
27
28
29class MockOutputApi(object):
30  class PresubmitResult(object):
31    def __init__(self, message, items=None, long_text=''):
32      self.message = message
33      self.items = items
34      self.long_text = long_text
35
36  class PresubmitError(PresubmitResult):
37    def __init__(self, message, items, long_text=''):
38      MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
39      self.type = 'error'
40
41  class PresubmitPromptWarning(PresubmitResult):
42    def __init__(self, message, items, long_text=''):
43      MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
44      self.type = 'warning'
45
46  class PresubmitNotifyResult(PresubmitResult):
47    def __init__(self, message, items, long_text=''):
48      MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
49      self.type = 'notify'
50
51  class PresubmitPromptOrNotify(PresubmitResult):
52    def __init__(self, message, items, long_text=''):
53      MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
54      self.type = 'promptOrNotify'
55
56
57class MockFile(object):
58  def __init__(self, local_path, new_contents):
59    self._local_path = local_path
60    self._new_contents = new_contents
61    self._changed_contents = [(i + 1, l) for i, l in enumerate(new_contents)]
62
63  def ChangedContents(self):
64    return self._changed_contents
65
66  def NewContents(self):
67    return self._new_contents
68
69  def LocalPath(self):
70    return self._local_path
71
72
73class MockChange(object):
74  def __init__(self, changed_files):
75    self._changed_files = changed_files
76
77  def LocalPaths(self):
78    return self._changed_files
79
80
81class HistogramOffByOneTest(unittest.TestCase):
82
83  # Take an input and make sure the problems found equals the expectation.
84  def simpleCheck(self, contents, expected_errors):
85    input_api = MockInputApi()
86    input_api.files.append(MockFile('test.cc', contents))
87    results = PRESUBMIT._CheckForHistogramOffByOne(input_api, MockOutputApi())
88    if expected_errors:
89      self.assertEqual(1, len(results))
90      self.assertEqual(expected_errors, len(results[0].items))
91    else:
92      self.assertEqual(0, len(results))
93
94  def testValid(self):
95    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFooMax + 1);', 0)
96
97  def testValidComments(self):
98    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", /*...*/ kFoo, /*...*/'
99                     'kFooMax + 1);', 0)
100
101  def testValidMultiLine(self):
102    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test",\n'
103                     '                          kFoo,\n'
104                     '                          kFooMax + 1);', 0)
105
106  def testValidMultiLineComments(self):
107    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test",  // This is the name\n'
108                     '                          kFoo,  /* The value */\n'
109                     '                          kFooMax + 1 /* The max */ );',
110                     0)
111
112  def testNoPlusOne(self):
113    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFooMax);', 1)
114
115  def testInvalidWithIgnore(self):
116    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFooMax); '
117                     '// PRESUBMIT_IGNORE_UMA_MAX', 0)
118
119  def testNoMax(self):
120    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFoo + 1);', 1)
121
122  def testNoMaxNoPlusOne(self):
123    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFoo);', 1)
124
125  def testMultipleErrors(self):
126    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFoo);\n'
127                     'printf("hello, world!");\n'
128                     'UMA_HISTOGRAM_ENUMERATION("test", kBar, kBarMax);', 2)
129
130  def testValidAndInvalid(self):
131    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFoo);\n'
132                     'UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFooMax + 1);'
133                     'UMA_HISTOGRAM_ENUMERATION("test", kBar, kBarMax);', 2)
134
135  def testInvalidMultiLine(self):
136    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test",\n'
137                     '                          kFoo,\n'
138                     '                          kFooMax + 2);', 1)
139
140  def testInvalidComments(self):
141    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", /*...*/, val, /*...*/,'
142                     'Max);\n', 1)
143
144  def testInvalidMultiLineComments(self):
145    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test",  // This is the name\n'
146                     '                          kFoo,  /* The value */\n'
147                     '                          kFooMax + 2 /* The max */ );',
148                     1)
149
150if __name__ == '__main__':
151  unittest.main()
152