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