diff options
author | Steve Block <steveblock@google.com> | 2010-02-05 14:27:46 +0000 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2010-02-15 10:49:50 +0000 |
commit | 5e2bc6953fe6923165b8a5d7679939693a1d58d6 (patch) | |
tree | 6ccb8c24bc2bf5e8f413e6cfae250b729b426631 /WebKitTools/Scripts/webkitpy/style | |
parent | 4a00f4fccc3cb7e9996749a05631f5d7b9de756e (diff) | |
download | external_webkit-5e2bc6953fe6923165b8a5d7679939693a1d58d6.zip external_webkit-5e2bc6953fe6923165b8a5d7679939693a1d58d6.tar.gz external_webkit-5e2bc6953fe6923165b8a5d7679939693a1d58d6.tar.bz2 |
Merge webkit.org at r54340 : Initial merge by git
Change-Id: Ib489d2ff91186ea3652522e1d586e54416a2cf44
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/style')
9 files changed, 489 insertions, 189 deletions
diff --git a/WebKitTools/Scripts/webkitpy/style/checker.py b/WebKitTools/Scripts/webkitpy/style/checker.py index faf954f..dc14ea3 100644 --- a/WebKitTools/Scripts/webkitpy/style/checker.py +++ b/WebKitTools/Scripts/webkitpy/style/checker.py @@ -37,6 +37,9 @@ import sys from .. style_references import parse_patch from error_handlers import DefaultStyleErrorHandler from error_handlers import PatchStyleErrorHandler +from filter import CategoryFilter +from processors.common import check_no_carriage_return +from processors.common import categories as CommonCategories from processors.cpp import CppProcessor from processors.text import TextProcessor @@ -106,10 +109,17 @@ SKIPPED_FILES_WITHOUT_WARNING = [ ] +# The maximum number of errors to report per file, per category. +# If a category is not a key, then it has no maximum. +MAX_REPORTS_PER_CATEGORY = { + "whitespace/carriage_return": 1 +} + + def style_categories(): """Return the set of all categories used by check-webkit-style.""" - # If other processors had categories, we would take their union here. - return CppProcessor.categories + # Take the union across all processors. + return CommonCategories.union(CppProcessor.categories) def webkit_argument_defaults(): @@ -191,79 +201,6 @@ Syntax: %(program_name)s [--verbose=#] [--git-commit=<SingleCommit>] [--output=v return usage -class CategoryFilter(object): - - """Filters whether to check style categories.""" - - def __init__(self, filter_rules=None): - """Create a category filter. - - This method performs argument validation but does not strip - leading or trailing white space. - - Args: - filter_rules: A list of strings that are filter rules, which - are strings beginning with the plus or minus - symbol (+/-). The list should include any - default filter rules at the beginning. - Defaults to the empty list. - - Raises: - ValueError: Invalid filter rule if a rule does not start with - plus ("+") or minus ("-"). - - """ - if filter_rules is None: - filter_rules = [] - - for rule in filter_rules: - if not (rule.startswith('+') or rule.startswith('-')): - raise ValueError('Invalid filter rule "%s": every rule ' - 'rule in the --filter flag must start ' - 'with + or -.' % rule) - - self._filter_rules = filter_rules - self._should_check_category = {} # Cached dictionary of category to True/False - - def __str__(self): - return ",".join(self._filter_rules) - - # Useful for unit testing. - def __eq__(self, other): - """Return whether this CategoryFilter instance is equal to another.""" - return self._filter_rules == other._filter_rules - - # Useful for unit testing. - def __ne__(self, other): - # Python does not automatically deduce from __eq__(). - return not (self == other) - - def should_check(self, category): - """Return whether the category should be checked. - - The rules for determining whether a category should be checked - are as follows. By default all categories should be checked. - Then apply the filter rules in order from first to last, with - later flags taking precedence. - - A filter rule applies to a category if the string after the - leading plus/minus (+/-) matches the beginning of the category - name. A plus (+) means the category should be checked, while a - minus (-) means the category should not be checked. - - """ - if category in self._should_check_category: - return self._should_check_category[category] - - should_check = True # All categories checked by default. - for rule in self._filter_rules: - if not category.startswith(rule[1:]): - continue - should_check = rule.startswith('+') - self._should_check_category[category] = should_check # Update cache. - return should_check - - # This class should not have knowledge of the flag key names. class ProcessorOptions(object): @@ -290,12 +227,19 @@ class ProcessorOptions(object): """ - def __init__(self, output_format="emacs", verbosity=1, filter=None, - git_commit=None, extra_flag_values=None): - if filter is None: - filter = CategoryFilter() + def __init__(self, + output_format="emacs", + verbosity=1, + filter=None, + max_reports_per_category=None, + git_commit=None, + extra_flag_values=None): if extra_flag_values is None: extra_flag_values = {} + if filter is None: + filter = CategoryFilter() + if max_reports_per_category is None: + max_reports_per_category = {} if output_format not in ("emacs", "vs7"): raise ValueError('Invalid "output_format" parameter: ' @@ -307,24 +251,27 @@ class ProcessorOptions(object): "value must be an integer between 1-5 inclusive. " 'Value given: "%s".' % verbosity) - self.output_format = output_format - self.verbosity = verbosity + self.extra_flag_values = extra_flag_values self.filter = filter self.git_commit = git_commit - self.extra_flag_values = extra_flag_values + self.max_reports_per_category = max_reports_per_category + self.output_format = output_format + self.verbosity = verbosity # Useful for unit testing. def __eq__(self, other): """Return whether this ProcessorOptions instance is equal to another.""" - if self.output_format != other.output_format: - return False - if self.verbosity != other.verbosity: + if self.extra_flag_values != other.extra_flag_values: return False if self.filter != other.filter: return False if self.git_commit != other.git_commit: return False - if self.extra_flag_values != other.extra_flag_values: + if self.max_reports_per_category != other.max_reports_per_category: + return False + if self.output_format != other.output_format: + return False + if self.verbosity != other.verbosity: return False return True @@ -568,8 +515,12 @@ class ArgumentParser(object): filter = CategoryFilter(filter_rules) - options = ProcessorOptions(output_format, verbosity, filter, - git_commit, extra_flag_values) + options = ProcessorOptions(extra_flag_values=extra_flag_values, + filter=filter, + git_commit=git_commit, + max_reports_per_category=MAX_REPORTS_PER_CATEGORY, + output_format=output_format, + verbosity=verbosity) return (filenames, options) @@ -720,35 +671,36 @@ class StyleChecker(object): # '\r\n' as in Windows), a warning is issued below if this file # is processed. if file_path == '-': - lines = codecs.StreamReaderWriter(sys.stdin, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace').read().split('\n') + file = codecs.StreamReaderWriter(sys.stdin, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace') else: - lines = codecs.open(file_path, 'r', 'utf8', 'replace').read().split('\n') + file = codecs.open(file_path, 'r', 'utf8', 'replace') - carriage_return_found = False - # Remove trailing '\r'. - for line_number in range(len(lines)): - if lines[line_number].endswith('\r'): - lines[line_number] = lines[line_number].rstrip('\r') - carriage_return_found = True + contents = file.read() except IOError: self._stderr_write("Skipping input '%s': Can't open for reading\n" % file_path) return - processor.process(lines) + lines = contents.split("\n") - if carriage_return_found and os.linesep != '\r\n': - # FIXME: Make sure this error also shows up when checking - # patches, if appropriate. + for line_number in range(len(lines)): + # FIXME: We should probably use the SVN "eol-style" property + # or a white list to decide whether or not to do + # the carriage-return check. Originally, we did the + # check only if (os.linesep != '\r\n'). # - # Use 0 for line_number since outputting only one error for - # potentially several lines. - handle_style_error(file_path, 0, 'whitespace/newline', 1, - 'One or more unexpected \\r (^M) found;' - 'better to use only a \\n') + # FIXME: As a minor optimization, we can have + # check_no_carriage_return() return whether + # the line ends with "\r". + check_no_carriage_return(lines[line_number], line_number, + handle_style_error) + if lines[line_number].endswith("\r"): + lines[line_number] = lines[line_number].rstrip("\r") + + processor.process(lines) def check_file(self, file_path, handle_style_error=None, process_file=None): """Check style in the given file. diff --git a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py b/WebKitTools/Scripts/webkitpy/style/checker_unittest.py index 4d6b2e7..814bd41 100755 --- a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py +++ b/WebKitTools/Scripts/webkitpy/style/checker_unittest.py @@ -37,67 +37,13 @@ import unittest import checker as style -from checker import CategoryFilter from checker import ProcessorDispatcher from checker import ProcessorOptions from checker import StyleChecker +from filter import CategoryFilter from processors.cpp import CppProcessor from processors.text import TextProcessor -class CategoryFilterTest(unittest.TestCase): - - """Tests CategoryFilter class.""" - - def test_init(self): - """Test __init__ constructor.""" - self.assertRaises(ValueError, CategoryFilter, ["no_prefix"]) - CategoryFilter() # No ValueError: works - CategoryFilter(["+"]) # No ValueError: works - CategoryFilter(["-"]) # No ValueError: works - - def test_str(self): - """Test __str__ "to string" operator.""" - filter = CategoryFilter(["+a", "-b"]) - self.assertEquals(str(filter), "+a,-b") - - def test_eq(self): - """Test __eq__ equality function.""" - filter1 = CategoryFilter(["+a", "+b"]) - filter2 = CategoryFilter(["+a", "+b"]) - filter3 = CategoryFilter(["+b", "+a"]) - - # == calls __eq__. - self.assertTrue(filter1 == filter2) - self.assertFalse(filter1 == filter3) # Cannot test with assertNotEqual. - - def test_ne(self): - """Test __ne__ inequality function.""" - # != calls __ne__. - # By default, __ne__ always returns true on different objects. - # Thus, just check the distinguishing case to verify that the - # code defines __ne__. - self.assertFalse(CategoryFilter() != CategoryFilter()) - - def test_should_check(self): - """Test should_check() method.""" - filter = CategoryFilter() - self.assertTrue(filter.should_check("everything")) - # Check a second time to exercise cache. - self.assertTrue(filter.should_check("everything")) - - filter = CategoryFilter(["-"]) - self.assertFalse(filter.should_check("anything")) - # Check a second time to exercise cache. - self.assertFalse(filter.should_check("anything")) - - filter = CategoryFilter(["-", "+ab"]) - self.assertTrue(filter.should_check("abc")) - self.assertFalse(filter.should_check("a")) - - filter = CategoryFilter(["+", "-ab"]) - self.assertFalse(filter.should_check("abc")) - self.assertTrue(filter.should_check("a")) - class ProcessorOptionsTest(unittest.TestCase): @@ -110,6 +56,7 @@ class ProcessorOptionsTest(unittest.TestCase): self.assertEquals(options.extra_flag_values, {}) self.assertEquals(options.filter, CategoryFilter()) self.assertEquals(options.git_commit, None) + self.assertEquals(options.max_reports_per_category, {}) self.assertEquals(options.output_format, "emacs") self.assertEquals(options.verbosity, 1) @@ -126,11 +73,13 @@ class ProcessorOptionsTest(unittest.TestCase): options = ProcessorOptions(extra_flag_values={"extra_value" : 2}, filter=CategoryFilter(["+"]), git_commit="commit", + max_reports_per_category={"category": 3}, output_format="vs7", verbosity=3) self.assertEquals(options.extra_flag_values, {"extra_value" : 2}) self.assertEquals(options.filter, CategoryFilter(["+"])) self.assertEquals(options.git_commit, "commit") + self.assertEquals(options.max_reports_per_category, {"category": 3}) self.assertEquals(options.output_format, "vs7") self.assertEquals(options.verbosity, 3) @@ -143,11 +92,14 @@ class ProcessorOptionsTest(unittest.TestCase): options = ProcessorOptions(extra_flag_values={"extra_value" : 1}, filter=CategoryFilter(["+"]), git_commit="commit", + max_reports_per_category={"category": 3}, output_format="vs7", verbosity=1) self.assertFalse(options == ProcessorOptions(extra_flag_values={"extra_value" : 2})) self.assertFalse(options == ProcessorOptions(filter=CategoryFilter(["-"]))) self.assertFalse(options == ProcessorOptions(git_commit="commit2")) + self.assertFalse(options == ProcessorOptions(max_reports_per_category= + {"category": 2})) self.assertFalse(options == ProcessorOptions(output_format="emacs")) self.assertFalse(options == ProcessorOptions(verbosity=2)) @@ -173,9 +125,9 @@ class ProcessorOptionsTest(unittest.TestCase): self.assertFalse(options.is_reportable("xyz", 3)) -class WebKitArgumentDefaultsTest(unittest.TestCase): +class GlobalVariablesTest(unittest.TestCase): - """Tests validity of default arguments used by check-webkit-style.""" + """Tests validity of the global variables.""" def defaults(self): return style.webkit_argument_defaults() @@ -206,6 +158,13 @@ class WebKitArgumentDefaultsTest(unittest.TestCase): # on valid arguments elsewhere. parser.parse([]) # arguments valid: no error or SystemExit + def test_max_reports_per_category(self): + """Check that MAX_REPORTS_PER_CATEGORY is valid.""" + categories = style.style_categories() + for category in style.MAX_REPORTS_PER_CATEGORY.iterkeys(): + self.assertTrue(category in categories, + 'Key "%s" is not a category' % category) + class ArgumentPrinterTest(unittest.TestCase): @@ -217,8 +176,11 @@ class ArgumentPrinterTest(unittest.TestCase): filter_rules=[], git_commit=None, extra_flag_values={}): filter = CategoryFilter(filter_rules) - return style.ProcessorOptions(output_format, verbosity, filter, - git_commit, extra_flag_values) + return style.ProcessorOptions(extra_flag_values=extra_flag_values, + filter=filter, + git_commit=git_commit, + output_format=output_format, + verbosity=verbosity) def test_to_flag_string(self): options = self._create_options('vs7', 5, ['+foo', '-bar'], 'git', diff --git a/WebKitTools/Scripts/webkitpy/style/error_handlers.py b/WebKitTools/Scripts/webkitpy/style/error_handlers.py index 54b1d76..31140de 100644 --- a/WebKitTools/Scripts/webkitpy/style/error_handlers.py +++ b/WebKitTools/Scripts/webkitpy/style/error_handlers.py @@ -32,7 +32,9 @@ Methods: Handle the occurrence of a style error. - Check whether the error is reportable. If so, report the details. + Check whether the error is reportable. If so, increment the total + error count and report the details. Note that error reporting can + be suppressed after reaching a certain number of reports. Args: line_number: The integer line number of the line containing the error. @@ -79,6 +81,28 @@ class DefaultStyleErrorHandler(object): self._options = options self._stderr_write = stderr_write + # A string to integer dictionary cache of the number of reportable + # errors per category passed to this instance. + self._category_totals = { } + + def _add_reportable_error(self, category): + """Increment the error count and return the new category total.""" + self._increment_error_count() # Increment the total. + + # Increment the category total. + if not category in self._category_totals: + self._category_totals[category] = 1 + else: + self._category_totals[category] += 1 + + return self._category_totals[category] + + def _max_reports(self, category): + """Return the maximum number of errors to report.""" + if not category in self._options.max_reports_per_category: + return None + return self._options.max_reports_per_category[category] + def __call__(self, line_number, category, confidence, message): """Handle the occurrence of a style error. @@ -88,13 +112,23 @@ class DefaultStyleErrorHandler(object): if not self._options.is_reportable(category, confidence): return - self._increment_error_count() + category_total = self._add_reportable_error(category) + + max_reports = self._max_reports(category) + + if (max_reports is not None) and (category_total > max_reports): + # Then suppress displaying the error. + return if self._options.output_format == 'vs7': format_string = "%s(%s): %s [%s] [%d]\n" else: format_string = "%s:%s: %s [%s] [%d]\n" + if category_total == max_reports: + format_string += ("Suppressing further [%s] reports for this " + "file.\n" % category) + self._stderr_write(format_string % (self._file_path, line_number, message, @@ -130,7 +164,7 @@ class PatchStyleErrorHandler(object): if not self._line_numbers: for line in self._diff.lines: # When deleted line is not set, it means that - # the line is newly added. + # the line is newly added (or modified). if not line[0]: self._line_numbers.add(line[1]) @@ -140,9 +174,9 @@ class PatchStyleErrorHandler(object): """Handle the occurrence of a style error. This function does not report errors occurring in lines not - modified or added. + marked as modified or added in the patch. - Args: see the DefaultStyleErrorHandler.__call__() documentation. + See the docstring of this module for more information. """ if line_number not in self._get_line_numbers(): diff --git a/WebKitTools/Scripts/webkitpy/style/error_handlers_unittest.py b/WebKitTools/Scripts/webkitpy/style/error_handlers_unittest.py index 6a91ff2..83bdbb9 100644 --- a/WebKitTools/Scripts/webkitpy/style/error_handlers_unittest.py +++ b/WebKitTools/Scripts/webkitpy/style/error_handlers_unittest.py @@ -50,9 +50,6 @@ class DefaultStyleErrorHandlerTest(StyleErrorHandlerTestBase): _category = "whitespace/tab" - def _options(self, output_format): - return ProcessorOptions(verbosity=3, output_format=output_format) - def _error_handler(self, options): file_path = "foo.h" return DefaultStyleErrorHandler(file_path, @@ -60,29 +57,28 @@ class DefaultStyleErrorHandlerTest(StyleErrorHandlerTestBase): self._mock_increment_error_count, self._mock_stderr_write) - def _prepare_call(self, output_format="emacs"): - """Return options after initializing.""" - options = self._options(output_format) - - # Test that count is initialized to zero. + def _check_initialized(self): + """Check that count and error messages are initialized.""" self.assertEquals(0, self._error_count) self.assertEquals("", self._error_messages) - return options - - def _call_error_handler(self, options, confidence): - """Handle an error with given confidence.""" - handle_error = self._error_handler(options) - + def _call(self, handle_error, options, confidence): + """Handle an error with the given error handler.""" line_number = 100 message = "message" handle_error(line_number, self._category, confidence, message) + def _call_error_handler(self, options, confidence): + """Handle an error using a new error handler.""" + handle_error = self._error_handler(options) + self._call(handle_error, options, confidence) + def test_call_non_reportable(self): """Test __call__() method with a non-reportable error.""" confidence = 1 - options = self._prepare_call() + options = ProcessorOptions(verbosity=3) + self._check_initialized() # Confirm the error is not reportable. self.assertFalse(options.is_reportable(self._category, confidence)) @@ -95,7 +91,8 @@ class DefaultStyleErrorHandlerTest(StyleErrorHandlerTestBase): def test_call_reportable_emacs(self): """Test __call__() method with a reportable error and emacs format.""" confidence = 5 - options = self._prepare_call("emacs") + options = ProcessorOptions(verbosity=3, output_format="emacs") + self._check_initialized() self._call_error_handler(options, confidence) @@ -106,7 +103,8 @@ class DefaultStyleErrorHandlerTest(StyleErrorHandlerTestBase): def test_call_reportable_vs7(self): """Test __call__() method with a reportable error and vs7 format.""" confidence = 5 - options = self._prepare_call("vs7") + options = ProcessorOptions(verbosity=3, output_format="vs7") + self._check_initialized() self._call_error_handler(options, confidence) @@ -114,6 +112,36 @@ class DefaultStyleErrorHandlerTest(StyleErrorHandlerTestBase): self.assertEquals(self._error_messages, "foo.h(100): message [whitespace/tab] [5]\n") + def test_call_max_reports_per_category(self): + """Test error report suppression in __call__() method.""" + confidence = 5 + options = ProcessorOptions(verbosity=3, + max_reports_per_category={self._category: 2}) + error_handler = self._error_handler(options) + + self._check_initialized() + + # First call: usual reporting. + self._call(error_handler, options, confidence) + self.assertEquals(1, self._error_count) + self.assertEquals(self._error_messages, + "foo.h:100: message [whitespace/tab] [5]\n") + + # Second call: suppression message reported. + self._error_messages = "" + self._call(error_handler, options, confidence) + self.assertEquals(2, self._error_count) + self.assertEquals(self._error_messages, + "foo.h:100: message [whitespace/tab] [5]\n" + "Suppressing further [%s] reports for this file.\n" + % self._category) + + # Third call: no report. + self._error_messages = "" + self._call(error_handler, options, confidence) + self.assertEquals(3, self._error_count) + self.assertEquals(self._error_messages, "") + class PatchStyleErrorHandlerTest(StyleErrorHandlerTestBase): diff --git a/WebKitTools/Scripts/webkitpy/style/filter.py b/WebKitTools/Scripts/webkitpy/style/filter.py new file mode 100644 index 0000000..1b41424 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/style/filter.py @@ -0,0 +1,97 @@ +# Copyright (C) 2010 Chris Jerdonek (chris.jerdonek@gmail.com) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Contains filter-related code.""" + + +class CategoryFilter(object): + + """Filters whether to check style categories.""" + + def __init__(self, filter_rules=None): + """Create a category filter. + + This method performs argument validation but does not strip + leading or trailing white space. + + Args: + filter_rules: A list of strings that are filter rules, which + are strings beginning with the plus or minus + symbol (+/-). The list should include any + default filter rules at the beginning. + Defaults to the empty list. + + Raises: + ValueError: Invalid filter rule if a rule does not start with + plus ("+") or minus ("-"). + + """ + if filter_rules is None: + filter_rules = [] + + for rule in filter_rules: + if not (rule.startswith('+') or rule.startswith('-')): + raise ValueError('Invalid filter rule "%s": every rule ' + 'rule in the --filter flag must start ' + 'with + or -.' % rule) + + self._filter_rules = filter_rules + self._should_check_category = {} # Cached dictionary of category to True/False + + def __str__(self): + return ",".join(self._filter_rules) + + # Useful for unit testing. + def __eq__(self, other): + """Return whether this CategoryFilter instance is equal to another.""" + return self._filter_rules == other._filter_rules + + # Useful for unit testing. + def __ne__(self, other): + # Python does not automatically deduce from __eq__(). + return not (self == other) + + def should_check(self, category): + """Return whether the category should be checked. + + The rules for determining whether a category should be checked + are as follows. By default all categories should be checked. + Then apply the filter rules in order from first to last, with + later flags taking precedence. + + A filter rule applies to a category if the string after the + leading plus/minus (+/-) matches the beginning of the category + name. A plus (+) means the category should be checked, while a + minus (-) means the category should not be checked. + + """ + if category in self._should_check_category: + return self._should_check_category[category] + + should_check = True # All categories checked by default. + for rule in self._filter_rules: + if not category.startswith(rule[1:]): + continue + should_check = rule.startswith('+') + self._should_check_category[category] = should_check # Update cache. + return should_check + diff --git a/WebKitTools/Scripts/webkitpy/style/filter_unittest.py b/WebKitTools/Scripts/webkitpy/style/filter_unittest.py new file mode 100644 index 0000000..0b12123 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/style/filter_unittest.py @@ -0,0 +1,84 @@ +# Copyright (C) 2010 Chris Jerdonek (chris.jerdonek@gmail.com) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Unit tests for filter.py.""" + + +import unittest + +from filter import CategoryFilter + + +class CategoryFilterTest(unittest.TestCase): + + """Tests CategoryFilter class.""" + + def test_init(self): + """Test __init__ constructor.""" + self.assertRaises(ValueError, CategoryFilter, ["no_prefix"]) + CategoryFilter() # No ValueError: works + CategoryFilter(["+"]) # No ValueError: works + CategoryFilter(["-"]) # No ValueError: works + + def test_str(self): + """Test __str__ "to string" operator.""" + filter = CategoryFilter(["+a", "-b"]) + self.assertEquals(str(filter), "+a,-b") + + def test_eq(self): + """Test __eq__ equality function.""" + filter1 = CategoryFilter(["+a", "+b"]) + filter2 = CategoryFilter(["+a", "+b"]) + filter3 = CategoryFilter(["+b", "+a"]) + + # == calls __eq__. + self.assertTrue(filter1 == filter2) + self.assertFalse(filter1 == filter3) # Cannot test with assertNotEqual. + + def test_ne(self): + """Test __ne__ inequality function.""" + # != calls __ne__. + # By default, __ne__ always returns true on different objects. + # Thus, just check the distinguishing case to verify that the + # code defines __ne__. + self.assertFalse(CategoryFilter() != CategoryFilter()) + + def test_should_check(self): + """Test should_check() method.""" + filter = CategoryFilter() + self.assertTrue(filter.should_check("everything")) + # Check a second time to exercise cache. + self.assertTrue(filter.should_check("everything")) + + filter = CategoryFilter(["-"]) + self.assertFalse(filter.should_check("anything")) + # Check a second time to exercise cache. + self.assertFalse(filter.should_check("anything")) + + filter = CategoryFilter(["-", "+ab"]) + self.assertTrue(filter.should_check("abc")) + self.assertFalse(filter.should_check("a")) + + filter = CategoryFilter(["+", "-ab"]) + self.assertFalse(filter.should_check("abc")) + self.assertTrue(filter.should_check("a")) + diff --git a/WebKitTools/Scripts/webkitpy/style/processors/common.py b/WebKitTools/Scripts/webkitpy/style/processors/common.py new file mode 100644 index 0000000..dbf4bea --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/style/processors/common.py @@ -0,0 +1,59 @@ +# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Supports style checking not specific to any one processor.""" + + +# FIXME: Test this list in the same way that the list of CppProcessor +# categories is tested, for example by checking that all of its +# elements appear in the unit tests. This should probably be done +# after moving the relevant cpp_unittest.ErrorCollector code +# into a shared location and refactoring appropriately. +categories = set([ + "whitespace/carriage_return", +]) + + +def check_no_carriage_return(line, line_number, error): + """Check that a line does not end with a carriage return. + + Returns true if the check is successful (i.e. if the line does not + end with a carriage return), and false otherwise. + + Args: + line: A string that is the line to check. + line_number: The line number. + error: The function to call with any errors found. + + """ + + if line.endswith("\r"): + error(line_number, + "whitespace/carriage_return", + 1, + "One or more unexpected \\r (^M) found; " + "better to use only a \\n") + return False + + return True + + diff --git a/WebKitTools/Scripts/webkitpy/style/processors/common_unittest.py b/WebKitTools/Scripts/webkitpy/style/processors/common_unittest.py new file mode 100644 index 0000000..9362b65 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/style/processors/common_unittest.py @@ -0,0 +1,82 @@ +# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Unit tests for common.py.""" + + +import unittest + +from common import check_no_carriage_return + + +# FIXME: The unit tests for the cpp, text, and common processors should +# share supporting test code. This can include, for example, the +# mock style error handling code and the code to check that all +# of a processor's categories are covered by the unit tests. +# Such shared code can be located in a shared test file, perhaps +# ilke this one. +class CarriageReturnTest(unittest.TestCase): + + """Tests check_no_carriage_return().""" + + _category = "whitespace/carriage_return" + _confidence = 1 + + def setUp(self): + self._style_errors = [] # The list of accumulated style errors. + + def _mock_style_error_handler(self, line_number, category, confidence, + message): + """Append the error information to the list of style errors.""" + error = (line_number, category, confidence, message) + self._style_errors.append(error) + + def assert_carriage_return(self, line, is_error): + """Call check_no_carriage_return() and assert the result.""" + line_number = 100 + handle_style_error = self._mock_style_error_handler + + check_no_carriage_return(line, line_number, handle_style_error) + + expected_message = ("One or more unexpected \\r (^M) found; " + "better to use only a \\n") + + if is_error: + expected_errors = [(line_number, self._category, self._confidence, + expected_message)] + self.assertEquals(self._style_errors, expected_errors) + else: + self.assertEquals(self._style_errors, []) + + def test_ends_with_carriage(self): + self.assert_carriage_return("carriage return\r", is_error=True) + + def test_ends_with_nothing(self): + self.assert_carriage_return("no carriage return", is_error=False) + + def test_ends_with_newline(self): + self.assert_carriage_return("no carriage return\n", is_error=False) + + def test_ends_with_carriage_newline(self): + # Check_no_carriage_return only() checks the final character. + self.assert_carriage_return("carriage\r in a string", is_error=False) + diff --git a/WebKitTools/Scripts/webkitpy/style/unittests.py b/WebKitTools/Scripts/webkitpy/style/unittests.py index 11c10e7..f8e3f71 100644 --- a/WebKitTools/Scripts/webkitpy/style/unittests.py +++ b/WebKitTools/Scripts/webkitpy/style/unittests.py @@ -37,5 +37,7 @@ import unittest from checker_unittest import * from error_handlers_unittest import * +from filter_unittest import * +from processors.common_unittest import * from processors.cpp_unittest import * from processors.text_unittest import * |