summaryrefslogtreecommitdiffstats
path: root/WebKitTools/Scripts/webkitpy/style/checker_unittest.py
diff options
context:
space:
mode:
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/style/checker_unittest.py')
-rwxr-xr-xWebKitTools/Scripts/webkitpy/style/checker_unittest.py1006
1 files changed, 547 insertions, 459 deletions
diff --git a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py b/WebKitTools/Scripts/webkitpy/style/checker_unittest.py
index e1c9baf..5254275 100755
--- a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/style/checker_unittest.py
@@ -34,103 +34,119 @@
"""Unit tests for style.py."""
+import logging
+import os
import unittest
import checker as style
+from webkitpy.style_references import LogTesting
+from webkitpy.style_references import TestLogStream
+from checker import _BASE_FILTER_RULES
+from checker import _MAX_REPORTS_PER_CATEGORY
from checker import _PATH_RULES_SPECIFIER as PATH_RULES_SPECIFIER
-from checker import style_categories
-from checker import ProcessorDispatcher
-from checker import ProcessorOptions
-from checker import StyleChecker
+from checker import _all_categories
+from checker import check_webkit_style_configuration
+from checker import check_webkit_style_parser
+from checker import configure_logging
+from checker import CheckerDispatcher
+from checker import ProcessorBase
+from checker import StyleProcessor
+from checker import StyleProcessorConfiguration
+from checkers.cpp import CppChecker
+from checkers.python import PythonChecker
+from checkers.text import TextChecker
+from error_handlers import DefaultStyleErrorHandler
from filter import validate_filter_rules
from filter import FilterConfiguration
-from processors.cpp import CppProcessor
-from processors.text import TextProcessor
+from optparser import ArgumentParser
+from optparser import CommandOptionValues
+from webkitpy.common.system.logtesting import LoggingTestCase
+from webkitpy.style.filereader import TextFileReader
-class ProcessorOptionsTest(unittest.TestCase):
+class ConfigureLoggingTestBase(unittest.TestCase):
- """Tests ProcessorOptions class."""
+ """Base class for testing configure_logging().
- def test_init(self):
- """Test __init__ constructor."""
- # Check default parameters.
- options = ProcessorOptions()
- self.assertEquals(options.extra_flag_values, {})
- self.assertEquals(options.filter_configuration, FilterConfiguration())
- self.assertEquals(options.git_commit, None)
- self.assertEquals(options.max_reports_per_category, {})
- self.assertEquals(options.output_format, "emacs")
- self.assertEquals(options.verbosity, 1)
-
- # Check argument validation.
- self.assertRaises(ValueError, ProcessorOptions, output_format="bad")
- ProcessorOptions(output_format="emacs") # No ValueError: works
- ProcessorOptions(output_format="vs7") # works
- self.assertRaises(ValueError, ProcessorOptions, verbosity=0)
- self.assertRaises(ValueError, ProcessorOptions, verbosity=6)
- ProcessorOptions(verbosity=1) # works
- ProcessorOptions(verbosity=5) # works
-
- # Check attributes.
- filter_configuration = FilterConfiguration(base_rules=["+"])
- options = ProcessorOptions(extra_flag_values={"extra_value" : 2},
- filter_configuration=filter_configuration,
- 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_configuration, filter_configuration)
- 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)
-
- def test_eq(self):
- """Test __eq__ equality function."""
- # == calls __eq__.
- self.assertTrue(ProcessorOptions() == ProcessorOptions())
-
- # Verify that a difference in any argument causes equality to fail.
- filter_configuration = FilterConfiguration(base_rules=["+"])
- options = ProcessorOptions(extra_flag_values={"extra_value" : 1},
- filter_configuration=filter_configuration,
- git_commit="commit",
- max_reports_per_category={"category": 3},
- output_format="vs7",
- verbosity=1)
- self.assertFalse(options == ProcessorOptions(extra_flag_values={"extra_value" : 2}))
- new_config = FilterConfiguration(base_rules=["-"])
- self.assertFalse(options ==
- ProcessorOptions(filter_configuration=new_config))
- 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))
-
- 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(ProcessorOptions() != ProcessorOptions())
+ Sub-classes should implement:
- def test_is_reportable(self):
- """Test is_reportable()."""
- filter_configuration = FilterConfiguration(base_rules=["-xyz"])
- options = ProcessorOptions(filter_configuration=filter_configuration,
- verbosity=3)
+ is_verbose: The is_verbose value to pass to configure_logging().
+
+ """
+
+ def setUp(self):
+ is_verbose = self.is_verbose
+
+ log_stream = TestLogStream(self)
+
+ # Use a logger other than the root logger or one prefixed with
+ # webkit so as not to conflict with test-webkitpy logging.
+ logger = logging.getLogger("unittest")
+
+ # Configure the test logger not to pass messages along to the
+ # root logger. This prevents test messages from being
+ # propagated to loggers used by test-webkitpy logging (e.g.
+ # the root logger).
+ logger.propagate = False
+
+ self._handlers = configure_logging(stream=log_stream, logger=logger,
+ is_verbose=is_verbose)
+ self._log = logger
+ self._log_stream = log_stream
+
+ def tearDown(self):
+ """Reset logging to its original state.
+
+ This method ensures that the logging configuration set up
+ for a unit test does not affect logging in other unit tests.
+
+ """
+ logger = self._log
+ for handler in self._handlers:
+ logger.removeHandler(handler)
+
+ def assert_log_messages(self, messages):
+ """Assert that the logged messages equal the given messages."""
+ self._log_stream.assertMessages(messages)
+
+
+class ConfigureLoggingTest(ConfigureLoggingTestBase):
+
+ """Tests the configure_logging() function."""
+
+ is_verbose = False
+
+ def test_warning_message(self):
+ self._log.warn("test message")
+ self.assert_log_messages(["WARNING: test message\n"])
+
+ def test_below_warning_message(self):
+ # We test the boundary case of a logging level equal to 29.
+ # In practice, we will probably only be calling log.info(),
+ # which corresponds to a logging level of 20.
+ level = logging.WARNING - 1 # Equals 29.
+ self._log.log(level, "test message")
+ self.assert_log_messages(["test message\n"])
+
+ def test_debug_message(self):
+ self._log.debug("test message")
+ self.assert_log_messages([])
+
+ def test_two_messages(self):
+ self._log.info("message1")
+ self._log.info("message2")
+ self.assert_log_messages(["message1\n", "message2\n"])
+
+
+class ConfigureLoggingVerboseTest(ConfigureLoggingTestBase):
- # Test verbosity
- self.assertTrue(options.is_reportable("abc", 3, "foo.h"))
- self.assertFalse(options.is_reportable("abc", 2, "foo.h"))
+ """Tests the configure_logging() function with is_verbose True."""
- # Test filter
- self.assertTrue(options.is_reportable("xy", 3, "foo.h"))
- self.assertFalse(options.is_reportable("xyz", 3, "foo.h"))
+ is_verbose = True
+
+ def test_debug_message(self):
+ self._log.debug("test message")
+ self.assert_log_messages(["unittest: DEBUG test message\n"])
class GlobalVariablesTest(unittest.TestCase):
@@ -138,18 +154,18 @@ class GlobalVariablesTest(unittest.TestCase):
"""Tests validity of the global variables."""
def _all_categories(self):
- return style.style_categories()
+ return _all_categories()
def defaults(self):
- return style.webkit_argument_defaults()
+ return style._check_webkit_style_defaults()
- def test_filter_rules(self):
+ def test_webkit_base_filter_rules(self):
+ base_filter_rules = _BASE_FILTER_RULES
defaults = self.defaults()
already_seen = []
- validate_filter_rules(defaults.base_filter_rules,
- self._all_categories())
+ validate_filter_rules(base_filter_rules, self._all_categories())
# Also do some additional checks.
- for rule in defaults.base_filter_rules:
+ for rule in base_filter_rules:
# Check no leading or trailing white space.
self.assertEquals(rule, rule.strip())
# All categories are on by default, so defaults should
@@ -161,274 +177,186 @@ class GlobalVariablesTest(unittest.TestCase):
def test_defaults(self):
"""Check that default arguments are valid."""
- defaults = self.defaults()
+ default_options = self.defaults()
# FIXME: We should not need to call parse() to determine
# whether the default arguments are valid.
- parser = style.ArgumentParser(defaults)
+ parser = ArgumentParser(all_categories=self._all_categories(),
+ base_filter_rules=[],
+ default_options=default_options)
# No need to test the return value here since we test parse()
# on valid arguments elsewhere.
- parser.parse([]) # arguments valid: no error or SystemExit
+ #
+ # The default options are valid: no error or SystemExit.
+ parser.parse(args=[])
def test_path_rules_specifier(self):
- all_categories = style_categories()
+ all_categories = self._all_categories()
for (sub_paths, path_rules) in PATH_RULES_SPECIFIER:
- self.assertTrue(isinstance(path_rules, tuple),
- "Checking: " + str(path_rules))
validate_filter_rules(path_rules, self._all_categories())
- # Try using the path specifier (as an "end-to-end" check).
config = FilterConfiguration(path_specific=PATH_RULES_SPECIFIER)
- self.assertTrue(config.should_check("xxx_any_category",
- "xxx_non_matching_path"))
- self.assertTrue(config.should_check("xxx_any_category",
- "WebKitTools/WebKitAPITest/"))
- self.assertFalse(config.should_check("build/include",
- "WebKitTools/WebKitAPITest/"))
- self.assertFalse(config.should_check("readability/naming",
- "WebKit/qt/tests/qwebelement/tst_qwebelement.cpp"))
+
+ def assertCheck(path, category):
+ """Assert that the given category should be checked."""
+ message = ('Should check category "%s" for path "%s".'
+ % (category, path))
+ self.assertTrue(config.should_check(category, path))
+
+ def assertNoCheck(path, category):
+ """Assert that the given category should not be checked."""
+ message = ('Should not check category "%s" for path "%s".'
+ % (category, path))
+ self.assertFalse(config.should_check(category, path), message)
+
+ assertCheck("random_path.cpp",
+ "build/include")
+ assertNoCheck("WebKitTools/WebKitAPITest/main.cpp",
+ "build/include")
+ assertNoCheck("WebKit/qt/QGVLauncher/main.cpp",
+ "build/include")
+ assertNoCheck("WebKit/qt/QGVLauncher/main.cpp",
+ "readability/streams")
+
+ assertCheck("random_path.cpp",
+ "readability/naming")
+ assertNoCheck("WebKit/gtk/webkit/webkit.h",
+ "readability/naming")
+ assertNoCheck("WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp",
+ "readability/null")
+ assertNoCheck("WebKit/efl/ewk/ewk_view.h",
+ "readability/naming")
+ assertNoCheck("WebCore/css/CSSParser.cpp",
+ "readability/naming")
+ assertNoCheck("WebKit/qt/tests/qwebelement/tst_qwebelement.cpp",
+ "readability/naming")
+ assertNoCheck(
+ "JavaScriptCore/qt/tests/qscriptengine/tst_qscriptengine.cpp",
+ "readability/naming")
+ assertNoCheck("WebCore/ForwardingHeaders/debugger/Debugger.h",
+ "build/header_guard")
+
+ # Third-party Python code: webkitpy/thirdparty
+ path = "WebKitTools/Scripts/webkitpy/thirdparty/mock.py"
+ assertNoCheck(path, "build/include")
+ assertNoCheck(path, "pep8/E401") # A random pep8 category.
+ assertCheck(path, "pep8/W191")
+ assertCheck(path, "pep8/W291")
+ assertCheck(path, "whitespace/carriage_return")
def test_max_reports_per_category(self):
- """Check that MAX_REPORTS_PER_CATEGORY is valid."""
+ """Check that _MAX_REPORTS_PER_CATEGORY is valid."""
all_categories = self._all_categories()
- for category in style.MAX_REPORTS_PER_CATEGORY.iterkeys():
+ for category in _MAX_REPORTS_PER_CATEGORY.iterkeys():
self.assertTrue(category in all_categories,
'Key "%s" is not a category' % category)
-class ArgumentPrinterTest(unittest.TestCase):
-
- """Tests the ArgumentPrinter class."""
-
- _printer = style.ArgumentPrinter()
-
- def _create_options(self,
- output_format='emacs',
- verbosity=3,
- user_rules=[],
- git_commit=None,
- extra_flag_values={}):
- filter_configuration = FilterConfiguration(user_rules=user_rules)
- return style.ProcessorOptions(extra_flag_values=extra_flag_values,
- filter_configuration=filter_configuration,
- git_commit=git_commit,
- output_format=output_format,
- verbosity=verbosity)
+class CheckWebKitStyleFunctionTest(unittest.TestCase):
+
+ """Tests the functions with names of the form check_webkit_style_*."""
+
+ def test_check_webkit_style_configuration(self):
+ # Exercise the code path to make sure the function does not error out.
+ option_values = CommandOptionValues()
+ configuration = check_webkit_style_configuration(option_values)
- def test_to_flag_string(self):
- options = self._create_options('vs7', 5, ['+foo', '-bar'], 'git',
- {'a': 0, 'z': 1})
- self.assertEquals('--a=0 --filter=+foo,-bar --git-commit=git '
- '--output=vs7 --verbose=5 --z=1',
- self._printer.to_flag_string(options))
-
- # This is to check that --filter and --git-commit do not
- # show up when not user-specified.
- options = self._create_options()
- self.assertEquals('--output=emacs --verbose=3',
- self._printer.to_flag_string(options))
-
-
-class ArgumentParserTest(unittest.TestCase):
-
- """Test the ArgumentParser class."""
-
- def _parse(self):
- """Return a default parse() function for testing."""
- return self._create_parser().parse
-
- def _create_defaults(self, default_output_format='vs7',
- default_verbosity=3,
- default_filter_rules=['-', '+whitespace']):
- """Return a default ArgumentDefaults instance for testing."""
- return style.ArgumentDefaults(default_output_format,
- default_verbosity,
- default_filter_rules)
-
- def _create_parser(self, defaults=None):
- """Return an ArgumentParser instance for testing."""
- def create_usage(_defaults):
- """Return a usage string for testing."""
- return "usage"
-
- def doc_print(message):
- # We do not want the usage string or style categories
- # to print during unit tests, so print nothing.
- return
-
- if defaults is None:
- defaults = self._create_defaults()
-
- return style.ArgumentParser(defaults, create_usage, doc_print)
-
- def test_parse_documentation(self):
- parse = self._parse()
-
- # FIXME: Test both the printing of the usage string and the
- # filter categories help.
-
- # Request the usage string.
- self.assertRaises(SystemExit, parse, ['--help'])
- # Request default filter rules and available style categories.
- self.assertRaises(SystemExit, parse, ['--filter='])
-
- def test_parse_bad_values(self):
- parse = self._parse()
-
- # Pass an unsupported argument.
- self.assertRaises(SystemExit, parse, ['--bad'])
-
- self.assertRaises(ValueError, parse, ['--verbose=bad'])
- self.assertRaises(ValueError, parse, ['--verbose=0'])
- self.assertRaises(ValueError, parse, ['--verbose=6'])
- parse(['--verbose=1']) # works
- parse(['--verbose=5']) # works
-
- self.assertRaises(ValueError, parse, ['--output=bad'])
- parse(['--output=vs7']) # works
-
- # Pass a filter rule not beginning with + or -.
- self.assertRaises(ValueError, parse, ['--filter=build'])
- parse(['--filter=+build']) # works
- # Pass files and git-commit at the same time.
- self.assertRaises(SystemExit, parse, ['--git-commit=', 'file.txt'])
- # Pass an extra flag already supported.
- self.assertRaises(ValueError, parse, [], ['filter='])
- parse([], ['extra=']) # works
- # Pass an extra flag with typo.
- self.assertRaises(SystemExit, parse, ['--extratypo='], ['extra='])
- parse(['--extra='], ['extra=']) # works
- self.assertRaises(ValueError, parse, [], ['extra=', 'extra='])
-
-
- def test_parse_default_arguments(self):
- parse = self._parse()
-
- (files, options) = parse([])
-
- self.assertEquals(files, [])
-
- self.assertEquals(options.output_format, 'vs7')
- self.assertEquals(options.verbosity, 3)
- self.assertEquals(options.filter_configuration,
- FilterConfiguration(base_rules=["-", "+whitespace"],
- path_specific=PATH_RULES_SPECIFIER))
- self.assertEquals(options.git_commit, None)
-
- def test_parse_explicit_arguments(self):
- parse = self._parse()
-
- # Pass non-default explicit values.
- (files, options) = parse(['--output=emacs'])
- self.assertEquals(options.output_format, 'emacs')
- (files, options) = parse(['--verbose=4'])
- self.assertEquals(options.verbosity, 4)
- (files, options) = parse(['--git-commit=commit'])
- self.assertEquals(options.git_commit, 'commit')
+ def test_check_webkit_style_parser(self):
+ # Exercise the code path to make sure the function does not error out.
+ parser = check_webkit_style_parser()
- # Pass user_rules.
- (files, options) = parse(['--filter=+build,-whitespace'])
- config = options.filter_configuration
- self.assertEquals(options.filter_configuration,
- FilterConfiguration(base_rules=["-", "+whitespace"],
- path_specific=PATH_RULES_SPECIFIER,
- user_rules=["+build", "-whitespace"]))
-
- # Pass spurious white space in user rules.
- (files, options) = parse(['--filter=+build, -whitespace'])
- self.assertEquals(options.filter_configuration,
- FilterConfiguration(base_rules=["-", "+whitespace"],
- path_specific=PATH_RULES_SPECIFIER,
- user_rules=["+build", "-whitespace"]))
-
- # Pass extra flag values.
- (files, options) = parse(['--extra'], ['extra'])
- self.assertEquals(options.extra_flag_values, {'--extra': ''})
- (files, options) = parse(['--extra='], ['extra='])
- self.assertEquals(options.extra_flag_values, {'--extra': ''})
- (files, options) = parse(['--extra=x'], ['extra='])
- self.assertEquals(options.extra_flag_values, {'--extra': 'x'})
-
- def test_parse_files(self):
- parse = self._parse()
-
- (files, options) = parse(['foo.cpp'])
- self.assertEquals(files, ['foo.cpp'])
- # Pass multiple files.
- (files, options) = parse(['--output=emacs', 'foo.cpp', 'bar.cpp'])
- self.assertEquals(files, ['foo.cpp', 'bar.cpp'])
+class CheckerDispatcherSkipTest(unittest.TestCase):
+ """Tests the "should skip" methods of the CheckerDispatcher class."""
+
+ def setUp(self):
+ self._dispatcher = CheckerDispatcher()
-class ProcessorDispatcherSkipTest(unittest.TestCase):
-
- """Tests the "should skip" methods of the ProcessorDispatcher class."""
-
def test_should_skip_with_warning(self):
"""Test should_skip_with_warning()."""
- dispatcher = ProcessorDispatcher()
-
# Check a non-skipped file.
- self.assertFalse(dispatcher.should_skip_with_warning("foo.txt"))
+ self.assertFalse(self._dispatcher.should_skip_with_warning("foo.txt"))
# Check skipped files.
paths_to_skip = [
"gtk2drawing.c",
- "gtk2drawing.h",
+ "gtkdrawing.h",
"JavaScriptCore/qt/api/qscriptengine_p.h",
"WebCore/platform/gtk/gtk2drawing.c",
- "WebCore/platform/gtk/gtk2drawing.h",
+ "WebCore/platform/gtk/gtkdrawing.h",
"WebKit/gtk/tests/testatk.c",
"WebKit/qt/Api/qwebpage.h",
"WebKit/qt/tests/qwebsecurityorigin/tst_qwebsecurityorigin.cpp",
]
for path in paths_to_skip:
- self.assertTrue(dispatcher.should_skip_with_warning(path),
+ self.assertTrue(self._dispatcher.should_skip_with_warning(path),
"Checking: " + path)
- def test_should_skip_without_warning(self):
- """Test should_skip_without_warning()."""
- dispatcher = ProcessorDispatcher()
-
- # Check a non-skipped file.
- self.assertFalse(dispatcher.should_skip_without_warning("foo.txt"))
-
- # Check skipped files.
- paths_to_skip = [
- # LayoutTests folder
- "LayoutTests/foo.txt",
- ]
+ def _assert_should_skip_without_warning(self, path, is_checker_none,
+ expected):
+ # Check the file type before asserting the return value.
+ checker = self._dispatcher.dispatch(file_path=path,
+ handle_style_error=None,
+ min_confidence=3)
+ message = 'while checking: %s' % path
+ self.assertEquals(checker is None, is_checker_none, message)
+ self.assertEquals(self._dispatcher.should_skip_without_warning(path),
+ expected, message)
+
+ def test_should_skip_without_warning__true(self):
+ """Test should_skip_without_warning() for True return values."""
+ # Check a file with NONE file type.
+ path = 'foo.asdf' # Non-sensical file extension.
+ self._assert_should_skip_without_warning(path,
+ is_checker_none=True,
+ expected=True)
+
+ # Check files with non-NONE file type. These examples must be
+ # drawn from the _SKIPPED_FILES_WITHOUT_WARNING configuration
+ # variable.
+ path = os.path.join('LayoutTests', 'foo.txt')
+ self._assert_should_skip_without_warning(path,
+ is_checker_none=False,
+ expected=True)
+
+ def test_should_skip_without_warning__false(self):
+ """Test should_skip_without_warning() for False return values."""
+ paths = ['foo.txt',
+ os.path.join('LayoutTests', 'ChangeLog'),
+ ]
- for path in paths_to_skip:
- self.assertTrue(dispatcher.should_skip_without_warning(path),
- "Checking: " + path)
+ for path in paths:
+ self._assert_should_skip_without_warning(path,
+ is_checker_none=False,
+ expected=False)
-class ProcessorDispatcherDispatchTest(unittest.TestCase):
+class CheckerDispatcherDispatchTest(unittest.TestCase):
- """Tests dispatch_processor() method of ProcessorDispatcher class."""
+ """Tests dispatch() method of CheckerDispatcher class."""
def mock_handle_style_error(self):
pass
- def dispatch_processor(self, file_path):
- """Call dispatch_processor() with the given file path."""
- dispatcher = ProcessorDispatcher()
- processor = dispatcher.dispatch_processor(file_path,
- self.mock_handle_style_error,
- verbosity=3)
- return processor
-
- def assert_processor_none(self, file_path):
- """Assert that the dispatched processor is None."""
- processor = self.dispatch_processor(file_path)
- self.assertTrue(processor is None, 'Checking: "%s"' % file_path)
-
- def assert_processor(self, file_path, expected_class):
- """Assert the type of the dispatched processor."""
- processor = self.dispatch_processor(file_path)
- got_class = processor.__class__
+ def dispatch(self, file_path):
+ """Call dispatch() with the given file path."""
+ dispatcher = CheckerDispatcher()
+ checker = dispatcher.dispatch(file_path,
+ self.mock_handle_style_error,
+ min_confidence=3)
+ return checker
+
+ def assert_checker_none(self, file_path):
+ """Assert that the dispatched checker is None."""
+ checker = self.dispatch(file_path)
+ self.assertTrue(checker is None, 'Checking: "%s"' % file_path)
+
+ def assert_checker(self, file_path, expected_class):
+ """Assert the type of the dispatched checker."""
+ checker = self.dispatch(file_path)
+ got_class = checker.__class__
self.assertEquals(got_class, expected_class,
'For path "%(file_path)s" got %(got_class)s when '
"expecting %(expected_class)s."
@@ -436,13 +364,17 @@ class ProcessorDispatcherDispatchTest(unittest.TestCase):
"got_class": got_class,
"expected_class": expected_class})
- def assert_processor_cpp(self, file_path):
- """Assert that the dispatched processor is a CppProcessor."""
- self.assert_processor(file_path, CppProcessor)
+ def assert_checker_cpp(self, file_path):
+ """Assert that the dispatched checker is a CppChecker."""
+ self.assert_checker(file_path, CppChecker)
+
+ def assert_checker_python(self, file_path):
+ """Assert that the dispatched checker is a PythonChecker."""
+ self.assert_checker(file_path, PythonChecker)
- def assert_processor_text(self, file_path):
- """Assert that the dispatched processor is a TextProcessor."""
- self.assert_processor(file_path, TextProcessor)
+ def assert_checker_text(self, file_path):
+ """Assert that the dispatched checker is a TextChecker."""
+ self.assert_checker(file_path, TextChecker)
def test_cpp_paths(self):
"""Test paths that should be checked as C++."""
@@ -454,228 +386,384 @@ class ProcessorDispatcherDispatchTest(unittest.TestCase):
]
for path in paths:
- self.assert_processor_cpp(path)
+ self.assert_checker_cpp(path)
- # Check processor attributes on a typical input.
+ # Check checker attributes on a typical input.
file_base = "foo"
file_extension = "c"
file_path = file_base + "." + file_extension
- self.assert_processor_cpp(file_path)
- processor = self.dispatch_processor(file_path)
- self.assertEquals(processor.file_extension, file_extension)
- self.assertEquals(processor.file_path, file_path)
- self.assertEquals(processor.handle_style_error, self.mock_handle_style_error)
- self.assertEquals(processor.verbosity, 3)
+ self.assert_checker_cpp(file_path)
+ checker = self.dispatch(file_path)
+ self.assertEquals(checker.file_extension, file_extension)
+ self.assertEquals(checker.file_path, file_path)
+ self.assertEquals(checker.handle_style_error, self.mock_handle_style_error)
+ self.assertEquals(checker.min_confidence, 3)
# Check "-" for good measure.
file_base = "-"
file_extension = ""
file_path = file_base
- self.assert_processor_cpp(file_path)
- processor = self.dispatch_processor(file_path)
- self.assertEquals(processor.file_extension, file_extension)
- self.assertEquals(processor.file_path, file_path)
+ self.assert_checker_cpp(file_path)
+ checker = self.dispatch(file_path)
+ self.assertEquals(checker.file_extension, file_extension)
+ self.assertEquals(checker.file_path, file_path)
+
+ def test_python_paths(self):
+ """Test paths that should be checked as Python."""
+ paths = [
+ "foo.py",
+ "WebKitTools/Scripts/modules/text_style.py",
+ ]
+
+ for path in paths:
+ self.assert_checker_python(path)
+
+ # Check checker attributes on a typical input.
+ file_base = "foo"
+ file_extension = "css"
+ file_path = file_base + "." + file_extension
+ self.assert_checker_text(file_path)
+ checker = self.dispatch(file_path)
+ self.assertEquals(checker.file_path, file_path)
+ self.assertEquals(checker.handle_style_error,
+ self.mock_handle_style_error)
def test_text_paths(self):
"""Test paths that should be checked as text."""
paths = [
"ChangeLog",
+ "ChangeLog-2009-06-16",
+ "foo.ac",
+ "foo.cc",
+ "foo.cgi",
"foo.css",
+ "foo.exp",
+ "foo.flex",
+ "foo.gyp",
+ "foo.gypi",
"foo.html",
"foo.idl",
+ "foo.in",
"foo.js",
"foo.mm",
"foo.php",
+ "foo.pl",
"foo.pm",
- "foo.py",
+ "foo.pri",
+ "foo.pro",
+ "foo.rb",
+ "foo.sh",
"foo.txt",
- "FooChangeLog.bak",
- "WebCore/ChangeLog",
- "WebCore/inspector/front-end/inspector.js",
- "WebKitTools/Scripts/check-webkit=style",
- "WebKitTools/Scripts/modules/text_style.py",
- ]
+ "foo.wm",
+ "foo.xhtml",
+ "foo.y",
+ os.path.join("WebCore", "ChangeLog"),
+ os.path.join("WebCore", "inspector", "front-end", "inspector.js"),
+ os.path.join("WebKitTools", "Scripts", "check-webkit-style"),
+ ]
for path in paths:
- self.assert_processor_text(path)
+ self.assert_checker_text(path)
- # Check processor attributes on a typical input.
+ # Check checker attributes on a typical input.
file_base = "foo"
file_extension = "css"
file_path = file_base + "." + file_extension
- self.assert_processor_text(file_path)
- processor = self.dispatch_processor(file_path)
- self.assertEquals(processor.file_path, file_path)
- self.assertEquals(processor.handle_style_error, self.mock_handle_style_error)
+ self.assert_checker_text(file_path)
+ checker = self.dispatch(file_path)
+ self.assertEquals(checker.file_path, file_path)
+ self.assertEquals(checker.handle_style_error, self.mock_handle_style_error)
def test_none_paths(self):
"""Test paths that have no file type.."""
paths = [
"Makefile",
+ "foo.asdf", # Non-sensical file extension.
"foo.png",
"foo.exe",
+ "foo.vcproj",
]
for path in paths:
- self.assert_processor_none(path)
+ self.assert_checker_none(path)
-class StyleCheckerTest(unittest.TestCase):
+class StyleProcessorConfigurationTest(unittest.TestCase):
- """Test the StyleChecker class.
+ """Tests the StyleProcessorConfiguration class."""
- Attributes:
- error_messages: A string containing all of the warning messages
- written to the mock_stderr_write method of
- this class.
-
- """
+ def setUp(self):
+ self._error_messages = []
+ """The messages written to _mock_stderr_write() of this class."""
def _mock_stderr_write(self, message):
- pass
+ self._error_messages.append(message)
+
+ def _style_checker_configuration(self, output_format="vs7"):
+ """Return a StyleProcessorConfiguration instance for testing."""
+ base_rules = ["-whitespace", "+whitespace/tab"]
+ filter_configuration = FilterConfiguration(base_rules=base_rules)
- def _style_checker(self, options):
- return StyleChecker(options, self._mock_stderr_write)
+ return StyleProcessorConfiguration(
+ filter_configuration=filter_configuration,
+ max_reports_per_category={"whitespace/newline": 1},
+ min_confidence=3,
+ output_format=output_format,
+ stderr_write=self._mock_stderr_write)
+
+ def test_init(self):
+ """Test the __init__() method."""
+ configuration = self._style_checker_configuration()
+
+ # Check that __init__ sets the "public" data attributes correctly.
+ self.assertEquals(configuration.max_reports_per_category,
+ {"whitespace/newline": 1})
+ self.assertEquals(configuration.stderr_write, self._mock_stderr_write)
+ self.assertEquals(configuration.min_confidence, 3)
+
+ def test_is_reportable(self):
+ """Test the is_reportable() method."""
+ config = self._style_checker_configuration()
+
+ self.assertTrue(config.is_reportable("whitespace/tab", 3, "foo.txt"))
+
+ # Test the confidence check code path by varying the confidence.
+ self.assertFalse(config.is_reportable("whitespace/tab", 2, "foo.txt"))
+
+ # Test the category check code path by varying the category.
+ self.assertFalse(config.is_reportable("whitespace/line", 4, "foo.txt"))
+
+ def _call_write_style_error(self, output_format):
+ config = self._style_checker_configuration(output_format=output_format)
+ config.write_style_error(category="whitespace/tab",
+ confidence_in_error=5,
+ file_path="foo.h",
+ line_number=100,
+ message="message")
+
+ def test_write_style_error_emacs(self):
+ """Test the write_style_error() method."""
+ self._call_write_style_error("emacs")
+ self.assertEquals(self._error_messages,
+ ["foo.h:100: message [whitespace/tab] [5]\n"])
+
+ def test_write_style_error_vs7(self):
+ """Test the write_style_error() method."""
+ self._call_write_style_error("vs7")
+ self.assertEquals(self._error_messages,
+ ["foo.h(100): message [whitespace/tab] [5]\n"])
+
+
+class StyleProcessor_EndToEndTest(LoggingTestCase):
+
+ """Test the StyleProcessor class with an emphasis on end-to-end tests."""
+
+ def setUp(self):
+ LoggingTestCase.setUp(self)
+ self._messages = []
+
+ def _mock_stderr_write(self, message):
+ """Save a message so it can later be asserted."""
+ self._messages.append(message)
def test_init(self):
"""Test __init__ constructor."""
- options = ProcessorOptions()
- style_checker = self._style_checker(options)
+ configuration = StyleProcessorConfiguration(
+ filter_configuration=FilterConfiguration(),
+ max_reports_per_category={},
+ min_confidence=3,
+ output_format="vs7",
+ stderr_write=self._mock_stderr_write)
+ processor = StyleProcessor(configuration)
- self.assertEquals(style_checker.error_count, 0)
- self.assertEquals(style_checker.options, options)
+ self.assertEquals(processor.error_count, 0)
+ self.assertEquals(self._messages, [])
+ def test_process(self):
+ configuration = StyleProcessorConfiguration(
+ filter_configuration=FilterConfiguration(),
+ max_reports_per_category={},
+ min_confidence=3,
+ output_format="vs7",
+ stderr_write=self._mock_stderr_write)
+ processor = StyleProcessor(configuration)
-class StyleCheckerCheckFileTest(unittest.TestCase):
+ processor.process(lines=['line1', 'Line with tab:\t'],
+ file_path='foo.txt')
+ self.assertEquals(processor.error_count, 1)
+ expected_messages = ['foo.txt(2): Line contains tab character. '
+ '[whitespace/tab] [5]\n']
+ self.assertEquals(self._messages, expected_messages)
- """Test the check_file() method of the StyleChecker class.
- The check_file() method calls its process_file parameter when
- given a file that should not be skipped.
+class StyleProcessor_CodeCoverageTest(LoggingTestCase):
- The "got_*" attributes of this class are the parameters passed
- to process_file by calls to check_file() made by this test
- class. These attributes allow us to check the parameter values
- passed internally to the process_file function.
+ """Test the StyleProcessor class with an emphasis on code coverage.
- Attributes:
- got_file_path: The file_path parameter passed by check_file()
- to its process_file parameter.
- got_handle_style_error: The handle_style_error parameter passed
- by check_file() to its process_file
- parameter.
- got_processor: The processor parameter passed by check_file() to
- its process_file parameter.
- warning_messages: A string containing all of the warning messages
- written to the mock_stderr_write method of
- this class.
+ This class makes heavy use of mock objects.
"""
- def setUp(self):
- self.got_file_path = None
- self.got_handle_style_error = None
- self.got_processor = None
- self.warning_messages = ""
- def mock_stderr_write(self, warning_message):
- self.warning_messages += warning_message
+ class MockDispatchedChecker(object):
- def mock_handle_style_error(self):
+ """A mock checker dispatched by the MockDispatcher."""
+
+ def __init__(self, file_path, min_confidence, style_error_handler):
+ self.file_path = file_path
+ self.min_confidence = min_confidence
+ self.style_error_handler = style_error_handler
+
+ def check(self, lines):
+ self.lines = lines
+
+ class MockDispatcher(object):
+
+ """A mock CheckerDispatcher class."""
+
+ def __init__(self):
+ self.dispatched_checker = None
+
+ def should_skip_with_warning(self, file_path):
+ return file_path.endswith('skip_with_warning.txt')
+
+ def should_skip_without_warning(self, file_path):
+ return file_path.endswith('skip_without_warning.txt')
+
+ def dispatch(self, file_path, style_error_handler, min_confidence):
+ if file_path.endswith('do_not_process.txt'):
+ return None
+
+ checker = StyleProcessor_CodeCoverageTest.MockDispatchedChecker(
+ file_path,
+ min_confidence,
+ style_error_handler)
+
+ # Save the dispatched checker so the current test case has a
+ # way to access and check it.
+ self.dispatched_checker = checker
+
+ return checker
+
+ def setUp(self):
+ LoggingTestCase.setUp(self)
+ # We can pass an error-message swallower here because error message
+ # output is tested instead in the end-to-end test case above.
+ configuration = StyleProcessorConfiguration(
+ filter_configuration=FilterConfiguration(),
+ max_reports_per_category={"whitespace/newline": 1},
+ min_confidence=3,
+ output_format="vs7",
+ stderr_write=self._swallow_stderr_message)
+
+ mock_carriage_checker_class = self._create_carriage_checker_class()
+ mock_dispatcher = self.MockDispatcher()
+ # We do not need to use a real incrementer here because error-count
+ # incrementing is tested instead in the end-to-end test case above.
+ mock_increment_error_count = self._do_nothing
+
+ processor = StyleProcessor(configuration=configuration,
+ mock_carriage_checker_class=mock_carriage_checker_class,
+ mock_dispatcher=mock_dispatcher,
+ mock_increment_error_count=mock_increment_error_count)
+
+ self._configuration = configuration
+ self._mock_dispatcher = mock_dispatcher
+ self._processor = processor
+
+ def _do_nothing(self):
+ # We provide this function so the caller can pass it to the
+ # StyleProcessor constructor. This lets us assert the equality of
+ # the DefaultStyleErrorHandler instance generated by the process()
+ # method with an expected instance.
pass
- def mock_process_file(self, processor, file_path, handle_style_error):
- """A mock _process_file().
+ def _swallow_stderr_message(self, message):
+ """Swallow a message passed to stderr.write()."""
+ # This is a mock stderr.write() for passing to the constructor
+ # of the StyleProcessorConfiguration class.
+ pass
- See the documentation for this class for more information
- on this function.
+ def _create_carriage_checker_class(self):
- """
- self.got_file_path = file_path
- self.got_handle_style_error = handle_style_error
- self.got_processor = processor
-
- def assert_attributes(self,
- expected_file_path,
- expected_handle_style_error,
- expected_processor,
- expected_warning_messages):
- """Assert that the attributes of this class equal the given values."""
- self.assertEquals(self.got_file_path, expected_file_path)
- self.assertEquals(self.got_handle_style_error, expected_handle_style_error)
- self.assertEquals(self.got_processor, expected_processor)
- self.assertEquals(self.warning_messages, expected_warning_messages)
-
- def call_check_file(self, file_path):
- """Call the check_file() method of a test StyleChecker instance."""
- # Confirm that the attributes are reset.
- self.assert_attributes(None, None, None, "")
-
- # Create a test StyleChecker instance.
- #
- # The verbosity attribute is the only ProcessorOptions
- # attribute that needs to be checked in this test.
- # This is because it is the only option is directly
- # passed to the constructor of a style processor.
- options = ProcessorOptions(verbosity=3)
+ # Create a reference to self with a new name so its name does not
+ # conflict with the self introduced below.
+ test_case = self
- style_checker = StyleChecker(options, self.mock_stderr_write)
+ class MockCarriageChecker(object):
- style_checker.check_file(file_path,
- self.mock_handle_style_error,
- self.mock_process_file)
+ """A mock carriage-return checker."""
- def test_check_file_on_skip_without_warning(self):
- """Test check_file() for a skipped-without-warning file."""
+ def __init__(self, style_error_handler):
+ self.style_error_handler = style_error_handler
- file_path = "LayoutTests/foo.txt"
+ # This gives the current test case access to the
+ # instantiated carriage checker.
+ test_case.carriage_checker = self
- dispatcher = ProcessorDispatcher()
- # Confirm that the input file is truly a skipped-without-warning file.
- self.assertTrue(dispatcher.should_skip_without_warning(file_path))
+ def check(self, lines):
+ # Save the lines so the current test case has a way to access
+ # and check them.
+ self.lines = lines
- # Check the outcome.
- self.call_check_file(file_path)
- self.assert_attributes(None, None, None, "")
+ return lines
- def test_check_file_on_skip_with_warning(self):
- """Test check_file() for a skipped-with-warning file."""
+ return MockCarriageChecker
- file_path = "gtk2drawing.c"
+ def test_should_process__skip_without_warning(self):
+ """Test should_process() for a skip-without-warning file."""
+ file_path = "foo/skip_without_warning.txt"
- dispatcher = ProcessorDispatcher()
- # Check that the input file is truly a skipped-with-warning file.
- self.assertTrue(dispatcher.should_skip_with_warning(file_path))
+ self.assertFalse(self._processor.should_process(file_path))
- # Check the outcome.
- self.call_check_file(file_path)
- self.assert_attributes(None, None, None,
- 'Ignoring "gtk2drawing.c": this file is exempt from the style guide.\n')
+ def test_should_process__skip_with_warning(self):
+ """Test should_process() for a skip-with-warning file."""
+ file_path = "foo/skip_with_warning.txt"
- def test_check_file_on_non_skipped(self):
+ self.assertFalse(self._processor.should_process(file_path))
- # We use a C++ file since by using a CppProcessor, we can check
- # that all of the possible information is getting passed to
- # process_file (in particular, the verbosity).
- file_base = "foo"
- file_extension = "cpp"
- file_path = file_base + "." + file_extension
+ self.assertLog(['WARNING: File exempt from style guide. '
+ 'Skipping: "foo/skip_with_warning.txt"\n'])
+
+ def test_should_process__true_result(self):
+ """Test should_process() for a file that should be processed."""
+ file_path = "foo/skip_process.txt"
- dispatcher = ProcessorDispatcher()
- # Check that the input file is truly a C++ file.
- self.assertEquals(dispatcher._file_type(file_path), style.FileType.CPP)
+ self.assertTrue(self._processor.should_process(file_path))
- # Check the outcome.
- self.call_check_file(file_path)
+ def test_process__checker_dispatched(self):
+ """Test the process() method for a path with a dispatched checker."""
+ file_path = 'foo.txt'
+ lines = ['line1', 'line2']
+ line_numbers = [100]
- expected_processor = CppProcessor(file_path, file_extension, self.mock_handle_style_error, 3)
+ expected_error_handler = DefaultStyleErrorHandler(
+ configuration=self._configuration,
+ file_path=file_path,
+ increment_error_count=self._do_nothing,
+ line_numbers=line_numbers)
- self.assert_attributes(file_path,
- self.mock_handle_style_error,
- expected_processor,
- "")
+ self._processor.process(lines=lines,
+ file_path=file_path,
+ line_numbers=line_numbers)
+ # Check that the carriage-return checker was instantiated correctly
+ # and was passed lines correctly.
+ carriage_checker = self.carriage_checker
+ self.assertEquals(carriage_checker.style_error_handler,
+ expected_error_handler)
+ self.assertEquals(carriage_checker.lines, ['line1', 'line2'])
-if __name__ == '__main__':
- import sys
+ # Check that the style checker was dispatched correctly and was
+ # passed lines correctly.
+ checker = self._mock_dispatcher.dispatched_checker
+ self.assertEquals(checker.file_path, 'foo.txt')
+ self.assertEquals(checker.min_confidence, 3)
+ self.assertEquals(checker.style_error_handler, expected_error_handler)
- unittest.main()
+ self.assertEquals(checker.lines, ['line1', 'line2'])
+ def test_process__no_checker_dispatched(self):
+ """Test the process() method for a path with no dispatched checker."""
+ path = os.path.join('foo', 'do_not_process.txt')
+ self.assertRaises(AssertionError, self._processor.process,
+ lines=['line1', 'line2'], file_path=path,
+ line_numbers=[100])