1# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions
5# are met:
6# 1.  Redistributions of source code must retain the above copyright
7#     notice, this list of conditions and the following disclaimer.
8# 2.  Redistributions in binary form must reproduce the above copyright
9#     notice, this list of conditions and the following disclaimer in the
10#     documentation and/or other materials provided with the distribution.
11#
12# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
13# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
16# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23"""Unit tests for error_handlers.py."""
24
25
26import unittest
27
28from checker import StyleProcessorConfiguration
29from error_handlers import DefaultStyleErrorHandler
30from filter import FilterConfiguration
31
32
33class DefaultStyleErrorHandlerTest(unittest.TestCase):
34
35    """Tests the DefaultStyleErrorHandler class."""
36
37    def setUp(self):
38        self._error_messages = []
39        self._error_count = 0
40
41    _category = "whitespace/tab"
42    """The category name for the tests in this class."""
43
44    _file_path = "foo.h"
45    """The file path for the tests in this class."""
46
47    def _mock_increment_error_count(self):
48        self._error_count += 1
49
50    def _mock_stderr_write(self, message):
51        self._error_messages.append(message)
52
53    def _style_checker_configuration(self):
54        """Return a StyleProcessorConfiguration instance for testing."""
55        base_rules = ["-whitespace", "+whitespace/tab"]
56        filter_configuration = FilterConfiguration(base_rules=base_rules)
57
58        return StyleProcessorConfiguration(
59                   filter_configuration=filter_configuration,
60                   max_reports_per_category={"whitespace/tab": 2},
61                   min_confidence=3,
62                   output_format="vs7",
63                   stderr_write=self._mock_stderr_write)
64
65    def _error_handler(self, configuration, line_numbers=None):
66        return DefaultStyleErrorHandler(configuration=configuration,
67                   file_path=self._file_path,
68                   increment_error_count=self._mock_increment_error_count,
69                   line_numbers=line_numbers)
70
71    def _check_initialized(self):
72        """Check that count and error messages are initialized."""
73        self.assertEquals(0, self._error_count)
74        self.assertEquals(0, len(self._error_messages))
75
76    def _call_error_handler(self, handle_error, confidence, line_number=100):
77        """Call the given error handler with a test error."""
78        handle_error(line_number=line_number,
79                     category=self._category,
80                     confidence=confidence,
81                     message="message")
82
83    def test_eq__true_return_value(self):
84        """Test the __eq__() method for the return value of True."""
85        handler1 = self._error_handler(configuration=None)
86        handler2 = self._error_handler(configuration=None)
87
88        self.assertTrue(handler1.__eq__(handler2))
89
90    def test_eq__false_return_value(self):
91        """Test the __eq__() method for the return value of False."""
92        def make_handler(configuration=self._style_checker_configuration(),
93                file_path='foo.txt', increment_error_count=lambda: True,
94                line_numbers=[100]):
95            return DefaultStyleErrorHandler(configuration=configuration,
96                       file_path=file_path,
97                       increment_error_count=increment_error_count,
98                       line_numbers=line_numbers)
99
100        handler = make_handler()
101
102        # Establish a baseline for our comparisons below.
103        self.assertTrue(handler.__eq__(make_handler()))
104
105        # Verify that a difference in any argument causes equality to fail.
106        self.assertFalse(handler.__eq__(make_handler(configuration=None)))
107        self.assertFalse(handler.__eq__(make_handler(file_path='bar.txt')))
108        self.assertFalse(handler.__eq__(make_handler(increment_error_count=None)))
109        self.assertFalse(handler.__eq__(make_handler(line_numbers=[50])))
110
111    def test_ne(self):
112        """Test the __ne__() method."""
113        # By default, __ne__ always returns true on different objects.
114        # Thus, check just the distinguishing case to verify that the
115        # code defines __ne__.
116        handler1 = self._error_handler(configuration=None)
117        handler2 = self._error_handler(configuration=None)
118
119        self.assertFalse(handler1.__ne__(handler2))
120
121    def test_non_reportable_error(self):
122        """Test __call__() with a non-reportable error."""
123        self._check_initialized()
124        configuration = self._style_checker_configuration()
125
126        confidence = 1
127        # Confirm the error is not reportable.
128        self.assertFalse(configuration.is_reportable(self._category,
129                                                     confidence,
130                                                     self._file_path))
131        error_handler = self._error_handler(configuration)
132        self._call_error_handler(error_handler, confidence)
133
134        self.assertEquals(0, self._error_count)
135        self.assertEquals([], self._error_messages)
136
137    # Also serves as a reportable error test.
138    def test_max_reports_per_category(self):
139        """Test error report suppression in __call__() method."""
140        self._check_initialized()
141        configuration = self._style_checker_configuration()
142        error_handler = self._error_handler(configuration)
143
144        confidence = 5
145
146        # First call: usual reporting.
147        self._call_error_handler(error_handler, confidence)
148        self.assertEquals(1, self._error_count)
149        self.assertEquals(1, len(self._error_messages))
150        self.assertEquals(self._error_messages,
151                          ["foo.h(100):  message  [whitespace/tab] [5]\n"])
152
153        # Second call: suppression message reported.
154        self._call_error_handler(error_handler, confidence)
155        # The "Suppressing further..." message counts as an additional
156        # message (but not as an addition to the error count).
157        self.assertEquals(2, self._error_count)
158        self.assertEquals(3, len(self._error_messages))
159        self.assertEquals(self._error_messages[-2],
160                          "foo.h(100):  message  [whitespace/tab] [5]\n")
161        self.assertEquals(self._error_messages[-1],
162                          "Suppressing further [whitespace/tab] reports "
163                          "for this file.\n")
164
165        # Third call: no report.
166        self._call_error_handler(error_handler, confidence)
167        self.assertEquals(3, self._error_count)
168        self.assertEquals(3, len(self._error_messages))
169
170    def test_line_numbers(self):
171        """Test the line_numbers parameter."""
172        self._check_initialized()
173        configuration = self._style_checker_configuration()
174        error_handler = self._error_handler(configuration,
175                                            line_numbers=[50])
176        confidence = 5
177
178        # Error on non-modified line: no error.
179        self._call_error_handler(error_handler, confidence, line_number=60)
180        self.assertEquals(0, self._error_count)
181        self.assertEquals([], self._error_messages)
182
183        # Error on modified line: error.
184        self._call_error_handler(error_handler, confidence, line_number=50)
185        self.assertEquals(1, self._error_count)
186        self.assertEquals(self._error_messages,
187                          ["foo.h(50):  message  [whitespace/tab] [5]\n"])
188