Changeset 53675 in webkit


Ignore:
Timestamp:
Jan 21, 2010 9:39:24 PM (14 years ago)
Author:
eric@webkit.org
Message:

2010-01-21 Chris Jerdonek <Chris Jerdonek>

Reviewed by Shinichiro Hamaji.

Refactored to move file name and file-reading related code
from cpp_style.py and text_style.py to checker.py.

https://bugs.webkit.org/show_bug.cgi?id=33775

  • Scripts/check-webkit-style:
    • Updates caused by changes to checker.py.
  • Scripts/webkitpy/style/checker.py:
    • Added SKIPPED_FILES_WITH_WARNING list.
    • Added SKIPPED_FILES_WITHOUT_WARNING list.
    • Added FileType class.
    • Added ProcessorDispatcher class.
    • In StyleChecker class:
      • Renamed process_patch() to check_patch().
      • Renamed process_file() to check_file().
      • Added _process_file().
      • Related refactoring.
      • Addressed check_patch() FIXME to share code with process_file().
  • Scripts/webkitpy/style/checker_unittest.py:
    • Added ProcessorDispatcherSkipTest class.
    • Added ProcessorDispatcherDispatchTest class.
    • Added StyleCheckerCheckFileTest class.
  • Scripts/webkitpy/style/cpp_style.py:
    • Renamed process_file_data() to _process_lines.
    • Removed process_file() (moved logic to checker.py).
    • Removed can_handle() (moved logic to checker.py).
    • Added CppProcessor class.
    • Removed is_exempt() (moved logic to checker.py).
    • Added process_file_data() back as a wrapper function.
  • Scripts/webkitpy/style/cpp_style_unittest.py:
    • Removed test_can_handle().
    • Removed test_is_exempt().
    • Added CppProcessorTest class.
  • Scripts/webkitpy/style/text_style.py:
    • Added TextProcessor class.
    • Removed process_file().
    • Removed can_handle().
  • Scripts/webkitpy/style/text_style_unittest.py:
    • Removed test_can_handle().
    • Added TextProcessorTest class.
Location:
trunk/WebKitTools
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebKitTools/ChangeLog

    r53667 r53675  
     12010-01-21  Chris Jerdonek  <cjerdonek@webkit.org>
     2
     3        Reviewed by Shinichiro Hamaji.
     4
     5        Refactored to move file name and file-reading related code
     6        from cpp_style.py and text_style.py to checker.py.
     7
     8        https://bugs.webkit.org/show_bug.cgi?id=33775
     9
     10        * Scripts/check-webkit-style:
     11          - Updates caused by changes to checker.py.
     12
     13        * Scripts/webkitpy/style/checker.py:
     14          - Added SKIPPED_FILES_WITH_WARNING list.
     15          - Added SKIPPED_FILES_WITHOUT_WARNING list.
     16          - Added FileType class.
     17          - Added ProcessorDispatcher class.
     18          - In StyleChecker class:
     19            - Renamed process_patch() to check_patch().
     20            - Renamed process_file() to check_file().
     21            - Added _process_file().
     22            - Related refactoring.
     23            - Addressed check_patch() FIXME to share code with process_file().
     24
     25        * Scripts/webkitpy/style/checker_unittest.py:
     26          - Added ProcessorDispatcherSkipTest class.
     27          - Added ProcessorDispatcherDispatchTest class.
     28          - Added StyleCheckerCheckFileTest class.
     29
     30        * Scripts/webkitpy/style/cpp_style.py:
     31          - Renamed process_file_data() to _process_lines.
     32          - Removed process_file() (moved logic to checker.py).
     33          - Removed can_handle() (moved logic to checker.py).
     34          - Added CppProcessor class.
     35          - Removed is_exempt() (moved logic to checker.py).
     36          - Added process_file_data() back as a wrapper function.
     37
     38        * Scripts/webkitpy/style/cpp_style_unittest.py:
     39          - Removed test_can_handle().
     40          - Removed test_is_exempt().
     41          - Added CppProcessorTest class.
     42
     43        * Scripts/webkitpy/style/text_style.py:
     44          - Added TextProcessor class.
     45          - Removed process_file().
     46          - Removed can_handle().
     47
     48        * Scripts/webkitpy/style/text_style_unittest.py:
     49          - Removed test_can_handle().
     50          - Added TextProcessorTest class.
     51
    1522010-01-21  Chris Jerdonek  <cjerdonek@webkit.org>
    253
  • trunk/WebKitTools/Scripts/check-webkit-style

    r53374 r53675  
    6969    if files:
    7070        for filename in files:
    71             style_checker.process_file(filename)
     71            style_checker.check_file(filename)
    7272
    7373    else:
     
    8585        else:
    8686            patch = scm.create_patch()
    87         style_checker.process_patch(patch)
     87        style_checker.check_patch(patch)
    8888
    8989    sys.stderr.write('Total errors found: %d\n' % style_checker.error_count)
  • trunk/WebKitTools/Scripts/webkitpy/style/checker.py

    r53382 r53675  
    3030"""Front end of some style-checker modules."""
    3131
    32 # FIXME: Move more code from cpp_style to here.
    33 
     32import codecs
    3433import getopt
    3534import os.path
    3635import sys
    3736
    38 import cpp_style
    39 import text_style
    4037from diff_parser import DiffParser
     38from cpp_style import CppProcessor
     39from text_style import TextProcessor
    4140
    4241
     
    159158
    160159
     160# Some files should be skipped when checking style. For example,
     161# WebKit maintains some files in Mozilla style on purpose to ease
     162# future merges.
     163#
     164# Include a warning for skipped files that are less obvious.
     165SKIPPED_FILES_WITH_WARNING = [
     166    # The Qt API and tests do not follow WebKit style.
     167    # They follow Qt style. :)
     168    "gtk2drawing.c", # WebCore/platform/gtk/gtk2drawing.c
     169    "gtk2drawing.h", # WebCore/platform/gtk/gtk2drawing.h
     170    "JavaScriptCore/qt/api/",
     171    "WebKit/gtk/tests/",
     172    "WebKit/qt/Api/",
     173    "WebKit/qt/tests/",
     174    ]
     175
     176
     177# Don't include a warning for skipped files that are more common
     178# and more obvious.
     179SKIPPED_FILES_WITHOUT_WARNING = [
     180    "LayoutTests/"
     181    ]
     182
     183
    161184def webkit_argument_defaults():
    162185    """Return the DefaultArguments instance for use by check-webkit-style."""
     
    620643
    621644
     645# Enum-like idiom
     646class FileType:
     647
     648    NONE = 1
     649    # Alphabetize remaining types
     650    CPP = 2
     651    TEXT = 3
     652
     653
     654class ProcessorDispatcher(object):
     655
     656    """Supports determining whether and how to check style, based on path."""
     657
     658    cpp_file_extensions = (
     659        'c',
     660        'cpp',
     661        'h',
     662        )
     663
     664    text_file_extensions = (
     665        'css',
     666        'html',
     667        'idl',
     668        'js',
     669        'mm',
     670        'php',
     671        'pm',
     672        'py',
     673        'txt',
     674        )
     675
     676    def _file_extension(self, file_path):
     677        """Return the file extension without the leading dot."""
     678        return os.path.splitext(file_path)[1].lstrip(".")
     679
     680    def should_skip_with_warning(self, file_path):
     681        """Return whether the given file should be skipped with a warning."""
     682        for skipped_file in SKIPPED_FILES_WITH_WARNING:
     683            if file_path.find(skipped_file) >= 0:
     684                return True
     685        return False
     686
     687    def should_skip_without_warning(self, file_path):
     688        """Return whether the given file should be skipped without a warning."""
     689        for skipped_file in SKIPPED_FILES_WITHOUT_WARNING:
     690            if file_path.find(skipped_file) >= 0:
     691                return True
     692        return False
     693
     694    def _file_type(self, file_path):
     695        """Return the file type corresponding to the given file."""
     696        file_extension = self._file_extension(file_path)
     697
     698        if (file_extension in self.cpp_file_extensions) or (file_path == '-'):
     699            # FIXME: Do something about the comment below and the issue it
     700            #        raises since cpp_style already relies on the extension.
     701            #
     702            # Treat stdin as C++. Since the extension is unknown when
     703            # reading from stdin, cpp_style tests should not rely on
     704            # the extension.
     705            return FileType.CPP
     706        elif ("ChangeLog" in file_path
     707              or "WebKitTools/Scripts/" in file_path
     708              or file_extension in self.text_file_extensions):
     709            return FileType.TEXT
     710        else:
     711            return FileType.NONE
     712
     713    def _create_processor(self, file_type, file_path, handle_style_error, verbosity):
     714        """Instantiate and return a style processor based on file type."""
     715        if file_type == FileType.NONE:
     716            processor = None
     717        elif file_type == FileType.CPP:
     718            file_extension = self._file_extension(file_path)
     719            processor = CppProcessor(file_path, file_extension, handle_style_error, verbosity)
     720        elif file_type == FileType.TEXT:
     721            processor = TextProcessor(file_path, handle_style_error)
     722        else:
     723            raise ValueError('Invalid file type "%(file_type)s": the only valid file types '
     724                             "are %(NONE)s, %(CPP)s, and %(TEXT)s."
     725                             % {"file_type": file_type,
     726                                "NONE": FileType.NONE,
     727                                "CPP": FileType.CPP,
     728                                "TEXT": FileType.TEXT})
     729
     730        return processor
     731
     732    def dispatch_processor(self, file_path, handle_style_error, verbosity):
     733        """Instantiate and return a style processor based on file path."""
     734        file_type = self._file_type(file_path)
     735
     736        processor = self._create_processor(file_type,
     737                                           file_path,
     738                                           handle_style_error,
     739                                           verbosity)
     740        return processor
     741
     742
    622743class StyleChecker(object):
    623744
     
    633754    """
    634755
    635     def __init__(self, options, write_error=sys.stderr.write):
     756    def __init__(self, options, stderr_write=None):
    636757        """Create a StyleChecker instance.
    637758
     
    640761          stderr_write: A function that takes a string as a parameter
    641762                        and that is called when a style error occurs.
    642 
    643         """
    644         self._write_error = write_error
     763                        Defaults to sys.stderr.write. This should be
     764                        used only for unit tests.
     765
     766        """
     767        if stderr_write is None:
     768            stderr_write = sys.stderr.write
     769
     770        self._stderr_write = stderr_write
     771        self.error_count = 0
    645772        self.options = options
    646         self.error_count = 0
    647 
    648     def _handle_error(self, filename, line_number, category, confidence, message):
     773
     774    def _handle_style_error(self, filename, line_number, category, confidence, message):
    649775        """Handle the occurrence of a style error while checking.
    650776
     
    676802            format_string = "%s:%s:  %s  [%s] [%d]\n"
    677803
    678         self._write_error(format_string % (filename, line_number, message,
    679                                            category, confidence))
    680 
    681     def process_file(self, filename):
    682         """Checks style for the specified file.
    683 
    684         If the specified filename is '-', applies cpp_style to the standard input.
    685 
    686         """
    687         if cpp_style.can_handle(filename) or filename == '-':
    688             cpp_style.process_file(filename, self._handle_error, self.options.verbosity)
    689         elif text_style.can_handle(filename):
    690             text_style.process_file(filename, self._handle_error)
    691 
    692     def process_patch(self, patch_string):
    693         """Does lint on a single patch.
     804        self._stderr_write(format_string % (filename, line_number, message,
     805                                            category, confidence))
     806
     807    def _process_file(self, processor, file_path, handle_style_error):
     808        """Process the file using the given processor."""
     809        try:
     810            # Support the UNIX convention of using "-" for stdin.  Note that
     811            # we are not opening the file with universal newline support
     812            # (which codecs doesn't support anyway), so the resulting lines do
     813            # contain trailing '\r' characters if we are reading a file that
     814            # has CRLF endings.
     815            # If after the split a trailing '\r' is present, it is removed
     816            # below. If it is not expected to be present (i.e. os.linesep !=
     817            # '\r\n' as in Windows), a warning is issued below if this file
     818            # is processed.
     819            if file_path == '-':
     820                lines = codecs.StreamReaderWriter(sys.stdin,
     821                                                  codecs.getreader('utf8'),
     822                                                  codecs.getwriter('utf8'),
     823                                                  'replace').read().split('\n')
     824            else:
     825                lines = codecs.open(file_path, 'r', 'utf8', 'replace').read().split('\n')
     826
     827            carriage_return_found = False
     828            # Remove trailing '\r'.
     829            for line_number in range(len(lines)):
     830                if lines[line_number].endswith('\r'):
     831                    lines[line_number] = lines[line_number].rstrip('\r')
     832                    carriage_return_found = True
     833
     834        except IOError:
     835            self._stderr_write("Skipping input '%s': Can't open for reading\n" % file_path)
     836            return
     837
     838        processor.process(lines)
     839
     840        if carriage_return_found and os.linesep != '\r\n':
     841            # FIXME: Make sure this error also shows up when checking
     842            #        patches, if appropriate.
     843            #
     844            # Use 0 for line_number since outputting only one error for
     845            # potentially several lines.
     846            handle_style_error(file_path, 0, 'whitespace/newline', 1,
     847                               'One or more unexpected \\r (^M) found;'
     848                               'better to use only a \\n')
     849
     850    def check_file(self, file_path, handle_style_error=None, process_file=None):
     851        """Check style in the given file.
    694852
    695853        Args:
    696           patch_string: A string of a patch.
     854          file_path: A string that is the path of the file to process.
     855          handle_style_error: The function to call when a style error
     856                              occurs. This parameter is meant for internal
     857                              use within this class. Defaults to the
     858                              style error handling method of this class.
     859          process_file: The function to call to process the file. This
     860                        parameter should be used only for unit tests.
     861                        Defaults to the file processing method of this class.
     862
     863        """
     864        if handle_style_error is None:
     865            handle_style_error = self._handle_style_error
     866
     867        if process_file is None:
     868            process_file = self._process_file
     869
     870        dispatcher = ProcessorDispatcher()
     871
     872        if dispatcher.should_skip_without_warning(file_path):
     873            return
     874        if dispatcher.should_skip_with_warning(file_path):
     875            self._stderr_write('Ignoring "%s": this file is exempt from the '
     876                               "style guide.\n" % file_path)
     877            return
     878
     879        verbosity = self.options.verbosity
     880        processor = dispatcher.dispatch_processor(file_path,
     881                                                  handle_style_error,
     882                                                  verbosity)
     883        process_file(processor, file_path, handle_style_error)
     884
     885    def check_patch(self, patch_string):
     886        """Check style in the given patch.
     887
     888        Args:
     889          patch_string: A string that is a patch string.
    697890
    698891        """
    699892        patch = DiffParser(patch_string.splitlines())
    700         for filename, diff in patch.files.iteritems():
    701             file_extension = os.path.splitext(filename)[1]
     893        for file_path, diff in patch.files.iteritems():
    702894            line_numbers = set()
    703895
    704             def error_for_patch(filename, line_number, category, confidence, message):
     896            def error_for_patch(file_path, line_number, category, confidence, message):
    705897                """Wrapper function of cpp_style.error for patches.
    706898
    707899                This function outputs errors only if the line number
    708900                corresponds to lines which are modified or added.
     901
    709902                """
    710903                if not line_numbers:
     
    715908                            line_numbers.add(line[1])
    716909
     910                # FIXME: Make sure errors having line number zero are
     911                #        logged -- like carriage-return errors.
    717912                if line_number in line_numbers:
    718                     self._handle_error(filename, line_number, category, confidence, message)
    719 
    720             # FIXME: Share this code with self.process_file().
    721             if cpp_style.can_handle(filename):
    722                 cpp_style.process_file(filename, error_for_patch, self.options.verbosity)
    723             elif text_style.can_handle(filename):
    724                 text_style.process_file(filename, error_for_patch)
     913                    self._handle_style_error(file_path, line_number, category, confidence, message)
     914
     915            self.check_file(file_path, handle_style_error=error_for_patch)
     916
  • trunk/WebKitTools/Scripts/webkitpy/style/checker_unittest.py

    r53374 r53675  
    3939import checker as style
    4040from checker import CategoryFilter
     41from checker import ProcessorDispatcher
    4142from checker import ProcessorOptions
    4243from checker import StyleChecker
     44from cpp_style import CppProcessor
     45from text_style import TextProcessor
    4346
    4447class CategoryFilterTest(unittest.TestCase):
     
    353356
    354357
     358class ProcessorDispatcherSkipTest(unittest.TestCase):
     359
     360    """Tests the "should skip" methods of the ProcessorDispatcher class."""
     361
     362    def test_should_skip_with_warning(self):
     363        """Test should_skip_with_warning()."""
     364        dispatcher = ProcessorDispatcher()
     365
     366        # Check a non-skipped file.
     367        self.assertFalse(dispatcher.should_skip_with_warning("foo.txt"))
     368
     369        # Check skipped files.
     370        paths_to_skip = [
     371           "gtk2drawing.c",
     372           "gtk2drawing.h",
     373           "JavaScriptCore/qt/api/qscriptengine_p.h",
     374           "WebCore/platform/gtk/gtk2drawing.c",
     375           "WebCore/platform/gtk/gtk2drawing.h",
     376           "WebKit/gtk/tests/testatk.c",
     377           "WebKit/qt/Api/qwebpage.h",
     378           "WebKit/qt/tests/qwebsecurityorigin/tst_qwebsecurityorigin.cpp",
     379            ]
     380
     381        for path in paths_to_skip:
     382            self.assertTrue(dispatcher.should_skip_with_warning(path),
     383                            "Checking: " + path)
     384
     385    def test_should_skip_without_warning(self):
     386        """Test should_skip_without_warning()."""
     387        dispatcher = ProcessorDispatcher()
     388
     389        # Check a non-skipped file.
     390        self.assertFalse(dispatcher.should_skip_without_warning("foo.txt"))
     391
     392        # Check skipped files.
     393        paths_to_skip = [
     394           # LayoutTests folder
     395           "LayoutTests/foo.txt",
     396            ]
     397
     398        for path in paths_to_skip:
     399            self.assertTrue(dispatcher.should_skip_without_warning(path),
     400                            "Checking: " + path)
     401
     402
     403class ProcessorDispatcherDispatchTest(unittest.TestCase):
     404
     405    """Tests dispatch_processor() method of ProcessorDispatcher class."""
     406
     407    def mock_handle_style_error(self):
     408        pass
     409
     410    def dispatch_processor(self, file_path):
     411        """Call dispatch_processor() with the given file path."""
     412        dispatcher = ProcessorDispatcher()
     413        processor = dispatcher.dispatch_processor(file_path,
     414                                                  self.mock_handle_style_error,
     415                                                  verbosity=3)
     416        return processor
     417
     418    def assert_processor_none(self, file_path):
     419        """Assert that the dispatched processor is None."""
     420        processor = self.dispatch_processor(file_path)
     421        self.assertTrue(processor is None, 'Checking: "%s"' % file_path)
     422
     423    def assert_processor(self, file_path, expected_class):
     424        """Assert the type of the dispatched processor."""
     425        processor = self.dispatch_processor(file_path)
     426        got_class = processor.__class__
     427        self.assertEquals(got_class, expected_class,
     428                          'For path "%(file_path)s" got %(got_class)s when '
     429                          "expecting %(expected_class)s."
     430                          % {"file_path": file_path,
     431                             "got_class": got_class,
     432                             "expected_class": expected_class})
     433
     434    def assert_processor_cpp(self, file_path):
     435        """Assert that the dispatched processor is a CppProcessor."""
     436        self.assert_processor(file_path, CppProcessor)
     437
     438    def assert_processor_text(self, file_path):
     439        """Assert that the dispatched processor is a TextProcessor."""
     440        self.assert_processor(file_path, TextProcessor)
     441
     442    def test_cpp_paths(self):
     443        """Test paths that should be checked as C++."""
     444        paths = [
     445            "-",
     446            "foo.c",
     447            "foo.cpp",
     448            "foo.h",
     449            ]
     450
     451        for path in paths:
     452            self.assert_processor_cpp(path)
     453
     454        # Check processor attributes on a typical input.
     455        file_base = "foo"
     456        file_extension = "c"
     457        file_path = file_base + "." + file_extension
     458        self.assert_processor_cpp(file_path)
     459        processor = self.dispatch_processor(file_path)
     460        self.assertEquals(processor.file_extension, file_extension)
     461        self.assertEquals(processor.file_path, file_path)
     462        self.assertEquals(processor.handle_style_error, self.mock_handle_style_error)
     463        self.assertEquals(processor.verbosity, 3)
     464        # Check "-" for good measure.
     465        file_base = "-"
     466        file_extension = ""
     467        file_path = file_base
     468        self.assert_processor_cpp(file_path)
     469        processor = self.dispatch_processor(file_path)
     470        self.assertEquals(processor.file_extension, file_extension)
     471        self.assertEquals(processor.file_path, file_path)
     472
     473    def test_text_paths(self):
     474        """Test paths that should be checked as text."""
     475        paths = [
     476           "ChangeLog",
     477           "foo.css",
     478           "foo.html",
     479           "foo.idl",
     480           "foo.js",
     481           "foo.mm",
     482           "foo.php",
     483           "foo.pm",
     484           "foo.py",
     485           "foo.txt",
     486           "FooChangeLog.bak",
     487           "WebCore/ChangeLog",
     488           "WebCore/inspector/front-end/inspector.js",
     489           "WebKitTools/Scripts/check-webkit=style",
     490           "WebKitTools/Scripts/modules/text_style.py",
     491            ]
     492
     493        for path in paths:
     494            self.assert_processor_text(path)
     495
     496        # Check processor attributes on a typical input.
     497        file_base = "foo"
     498        file_extension = "css"
     499        file_path = file_base + "." + file_extension
     500        self.assert_processor_text(file_path)
     501        processor = self.dispatch_processor(file_path)
     502        self.assertEquals(processor.file_path, file_path)
     503        self.assertEquals(processor.handle_style_error, self.mock_handle_style_error)
     504
     505    def test_none_paths(self):
     506        """Test paths that have no file type.."""
     507        paths = [
     508           "Makefile",
     509           "foo.png",
     510           "foo.exe",
     511            ]
     512
     513        for path in paths:
     514            self.assert_processor_none(path)
     515
     516
    355517class StyleCheckerTest(unittest.TestCase):
    356518
     
    358520
    359521    Attributes:
    360       error_message: A string that is the last error message reported
    361                      by the test StyleChecker instance.
     522      error_messages: A string containing all of the warning messages
     523                      written to the mock_stderr_write method of
     524                      this class.
    362525
    363526    """
    364527
    365     error_message = ""
    366 
    367     def mock_write_error(self, error_message):
    368         """Store an error message without printing it."""
    369         self.error_message = error_message
     528    def setUp(self):
     529        self.error_messages = ""
     530
     531    def mock_stderr_write(self, error_message):
     532        """A mock sys.stderr.write."""
     533        self.error_messages = error_message
    370534        pass
    371535
     536    def mock_handle_style_error(self):
     537        pass
     538
    372539    def style_checker(self, options):
    373         return StyleChecker(options, self.mock_write_error)
     540        return StyleChecker(options, self.mock_stderr_write)
    374541
    375542    def test_init(self):
     
    382549
    383550    def write_sample_error(self, style_checker, error_confidence):
    384         """Write an error to the given style_checker."""
    385         style_checker._handle_error(filename="filename",
    386                                     line_number=1,
    387                                     category="category",
    388                                     confidence=error_confidence,
    389                                     message="message")
    390 
    391     def test_handle_error(self):
    392         """Test _handler_error() function."""
     551        """Write an error to the given style checker."""
     552        style_checker._handle_style_error(filename="filename",
     553                                          line_number=1,
     554                                          category="category",
     555                                          confidence=error_confidence,
     556                                          message="message")
     557
     558    def test_handle_style_error(self):
     559        """Test _handle_style_error() function."""
    393560        options = ProcessorOptions(output_format="emacs",
    394561                                   verbosity=3)
     
    397564        # Verify initialized properly.
    398565        self.assertEquals(style_checker.error_count, 0)
    399         self.assertEquals(self.error_message, "")
     566        self.assertEquals(self.error_messages, "")
    400567
    401568        # Check that should_print_error is getting called appropriately.
    402569        self.write_sample_error(style_checker, 2)
    403570        self.assertEquals(style_checker.error_count, 0) # Error confidence too low.
    404         self.assertEquals(self.error_message, "")
     571        self.assertEquals(self.error_messages, "")
    405572
    406573        self.write_sample_error(style_checker, 3)
    407574        self.assertEquals(style_checker.error_count, 1) # Error confidence just high enough.
    408         self.assertEquals(self.error_message, "filename:1:  message  [category] [3]\n")
     575        self.assertEquals(self.error_messages, "filename:1:  message  [category] [3]\n")
     576
     577        # Clear previous errors.
     578        self.error_messages = ""
    409579
    410580        # Check "vs7" output format.
     
    412582        self.write_sample_error(style_checker, 3)
    413583        self.assertEquals(style_checker.error_count, 2) # Error confidence just high enough.
    414         self.assertEquals(self.error_message, "filename(1):  message  [category] [3]\n")
     584        self.assertEquals(self.error_messages, "filename(1):  message  [category] [3]\n")
     585
     586
     587class StyleCheckerCheckFileTest(unittest.TestCase):
     588
     589    """Test the check_file() method of the StyleChecker class.
     590
     591    The check_file() method calls its process_file parameter when
     592    given a file that should not be skipped.
     593
     594    The "got_*" attributes of this class are the parameters passed
     595    to process_file by calls to check_file() made by this test
     596    class. These attributes allow us to check the parameter values
     597    passed internally to the process_file function.
     598
     599    Attributes:
     600      got_file_path: The file_path parameter passed by check_file()
     601                     to its process_file parameter.
     602      got_handle_style_error: The handle_style_error parameter passed
     603                              by check_file() to its process_file
     604                              parameter.
     605      got_processor: The processor parameter passed by check_file() to
     606                     its process_file parameter.
     607      warning_messages: A string containing all of the warning messages
     608                        written to the mock_stderr_write method of
     609                        this class.
     610
     611    """
     612    def setUp(self):
     613        self.got_file_path = None
     614        self.got_handle_style_error = None
     615        self.got_processor = None
     616        self.warning_messages = ""
     617
     618    def mock_stderr_write(self, warning_message):
     619        self.warning_messages += warning_message
     620
     621    def mock_handle_style_error(self):
     622        pass
     623
     624    def mock_process_file(self, processor, file_path, handle_style_error):
     625        """A mock _process_file().
     626
     627        See the documentation for this class for more information
     628        on this function.
     629
     630        """
     631        self.got_file_path = file_path
     632        self.got_handle_style_error = handle_style_error
     633        self.got_processor = processor
     634
     635    def assert_attributes(self,
     636                          expected_file_path,
     637                          expected_handle_style_error,
     638                          expected_processor,
     639                          expected_warning_messages):
     640        """Assert that the attributes of this class equal the given values."""
     641        self.assertEquals(self.got_file_path, expected_file_path)
     642        self.assertEquals(self.got_handle_style_error, expected_handle_style_error)
     643        self.assertEquals(self.got_processor, expected_processor)
     644        self.assertEquals(self.warning_messages, expected_warning_messages)
     645
     646    def call_check_file(self, file_path):
     647        """Call the check_file() method of a test StyleChecker instance."""
     648        # Confirm that the attributes are reset.
     649        self.assert_attributes(None, None, None, "")
     650
     651        # Create a test StyleChecker instance.
     652        #
     653        # The verbosity attribute is the only ProcessorOptions
     654        # attribute that needs to be checked in this test.
     655        # This is because it is the only option is directly
     656        # passed to the constructor of a style processor.
     657        options = ProcessorOptions(verbosity=3)
     658
     659        style_checker = StyleChecker(options, self.mock_stderr_write)
     660
     661        style_checker.check_file(file_path,
     662                                 self.mock_handle_style_error,
     663                                 self.mock_process_file)
     664
     665    def test_check_file_on_skip_without_warning(self):
     666        """Test check_file() for a skipped-without-warning file."""
     667
     668        file_path = "LayoutTests/foo.txt"
     669
     670        dispatcher = ProcessorDispatcher()
     671        # Confirm that the input file is truly a skipped-without-warning file.
     672        self.assertTrue(dispatcher.should_skip_without_warning(file_path))
     673
     674        # Check the outcome.
     675        self.call_check_file(file_path)
     676        self.assert_attributes(None, None, None, "")
     677
     678    def test_check_file_on_skip_with_warning(self):
     679        """Test check_file() for a skipped-with-warning file."""
     680
     681        file_path = "gtk2drawing.c"
     682
     683        dispatcher = ProcessorDispatcher()
     684        # Check that the input file is truly a skipped-with-warning file.
     685        self.assertTrue(dispatcher.should_skip_with_warning(file_path))
     686
     687        # Check the outcome.
     688        self.call_check_file(file_path)
     689        self.assert_attributes(None, None, None,
     690                               'Ignoring "gtk2drawing.c": this file is exempt from the style guide.\n')
     691
     692    def test_check_file_on_non_skipped(self):
     693
     694        # We use a C++ file since by using a CppProcessor, we can check
     695        # that all of the possible information is getting passed to
     696        # process_file (in particular, the verbosity).
     697        file_base = "foo"
     698        file_extension = "cpp"
     699        file_path = file_base + "." + file_extension
     700
     701        dispatcher = ProcessorDispatcher()
     702        # Check that the input file is truly a C++ file.
     703        self.assertEquals(dispatcher._file_type(file_path), style.FileType.CPP)
     704
     705        # Check the outcome.
     706        self.call_check_file(file_path)
     707
     708        expected_processor = CppProcessor(file_path, file_extension, self.mock_handle_style_error, 3)
     709
     710        self.assert_attributes(file_path,
     711                               self.mock_handle_style_error,
     712                               expected_processor,
     713                               "")
    415714
    416715
     
    419718
    420719    unittest.main()
     720
  • trunk/WebKitTools/Scripts/webkitpy/style/cpp_style.py

    r53636 r53675  
    55# Copyright (C) 2009 Torch Mobile Inc.
    66# Copyright (C) 2009 Apple Inc. All rights reserved.
     7# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
    78#
    89# Redistribution and use in source and binary forms, with or without
     
    28212822
    28222823
    2823 def process_file_data(filename, file_extension, lines, error, verbosity):
     2824def _process_lines(filename, file_extension, lines, error, verbosity):
    28242825    """Performs lint checks and reports any errors to the given error function.
    28252826
     
    28602861
    28612862
    2862 def process_file(filename, error, verbosity):
    2863     """Performs cpp_style on a single file.
    2864 
    2865     Args:
    2866       filename: The name of the file to parse.
    2867       error: The function to call with any errors found.
    2868       verbosity: An integer that is the verbosity level to use while
    2869                  checking style.
    2870     """
    2871     try:
    2872         # Support the UNIX convention of using "-" for stdin.  Note that
    2873         # we are not opening the file with universal newline support
    2874         # (which codecs doesn't support anyway), so the resulting lines do
    2875         # contain trailing '\r' characters if we are reading a file that
    2876         # has CRLF endings.
    2877         # If after the split a trailing '\r' is present, it is removed
    2878         # below. If it is not expected to be present (i.e. os.linesep !=
    2879         # '\r\n' as in Windows), a warning is issued below if this file
    2880         # is processed.
    2881 
    2882         if filename == '-':
    2883             lines = codecs.StreamReaderWriter(sys.stdin,
    2884                                               codecs.getreader('utf8'),
    2885                                               codecs.getwriter('utf8'),
    2886                                               'replace').read().split('\n')
    2887         else:
    2888             lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
    2889 
    2890         carriage_return_found = False
    2891         # Remove trailing '\r'.
    2892         for line_number in range(len(lines)):
    2893             if lines[line_number].endswith('\r'):
    2894                 lines[line_number] = lines[line_number].rstrip('\r')
    2895                 carriage_return_found = True
    2896 
    2897     except IOError:
    2898         sys.stderr.write(
    2899             "Skipping input '%s': Can't open for reading\n" % filename)
    2900         return
    2901 
    2902     # Note, if no dot is found, this will give the entire filename as the ext.
    2903     file_extension = filename[filename.rfind('.') + 1:]
    2904 
    2905     # When reading from stdin, the extension is unknown, so no cpp_style tests
    2906     # should rely on the extension.
    2907     if (filename != '-' and not can_handle(filename)):
    2908         sys.stderr.write('Ignoring %s; not a .cpp, .c or .h file\n' % filename)
    2909     elif is_exempt(filename):
    2910         sys.stderr.write('Ignoring %s; This file is exempt from the style guide.\n' % filename)
    2911     else:
    2912         process_file_data(filename, file_extension, lines, error, verbosity)
    2913         if carriage_return_found and os.linesep != '\r\n':
    2914             # Use 0 for line_number since outputing only one error for potentially
    2915             # several lines.
    2916             error(filename, 0, 'whitespace/newline', 1,
    2917                   'One or more unexpected \\r (^M) found;'
    2918                   'better to use only a \\n')
    2919 
    2920 
    2921 def can_handle(filename):
    2922     """Checks if this module supports for the specified file type.
    2923 
    2924     Args:
    2925       filename: A filename. It may contain directory names.
    2926      """
    2927     return os.path.splitext(filename)[1] in ('.h', '.cpp', '.c')
    2928 
    2929 
    2930 def is_exempt(filename):
    2931     """Checks if the given file is exempt from the style guide.  For example,
    2932     some files are purposefully mantained in Mozilla style to ease future
    2933     merges.
    2934 
    2935     Args:
    2936       filename: A filename. It may contain directory names.
    2937      """
    2938     if (filename.find('WebKit/qt/Api/') >= 0
    2939         or filename.find('JavaScriptCore/qt/api/') >= 0
    2940         or filename.find('WebKit/qt/tests/') >= 0
    2941         or filename.find('WebKit/gtk/tests/') >= 0):
    2942         # The Qt API and the Qt and Gtk tests do not follow WebKit style.
     2863class CppProcessor(object):
     2864
     2865    """Processes C++ lines for checking style."""
     2866
     2867    def __init__(self, file_path, file_extension, handle_style_error, verbosity):
     2868        """Create a CppProcessor instance.
     2869
     2870        Args:
     2871          file_extension: A string that is the file extension, without
     2872                          the leading dot.
     2873
     2874        """
     2875        self.file_extension = file_extension
     2876        self.file_path = file_path
     2877        self.handle_style_error = handle_style_error
     2878        self.verbosity = verbosity
     2879
     2880    # Useful for unit testing.
     2881    def __eq__(self, other):
     2882        """Return whether this CppProcessor instance is equal to another."""
     2883        if self.file_extension != other.file_extension:
     2884            return False
     2885        if self.file_path != other.file_path:
     2886            return False
     2887        if self.handle_style_error != other.handle_style_error:
     2888            return False
     2889        if self.verbosity != other.verbosity:
     2890            return False
     2891
    29432892        return True
    29442893
    2945     return os.path.basename(filename) in (
    2946         'gtk2drawing.c',
    2947         'gtk2drawing.h',
    2948     )
     2894    # Useful for unit testing.
     2895    def __ne__(self, other):
     2896        # Python does not automatically deduce __ne__() from __eq__().
     2897        return not self.__eq__(other)
     2898
     2899    def process(self, lines):
     2900        _process_lines(self.file_path, self.file_extension, lines,
     2901                       self.handle_style_error, self.verbosity)
     2902
     2903
     2904# FIXME: Remove this function (requires refactoring unit tests).
     2905def process_file_data(filename, file_extension, lines, error, verbosity):
     2906    processor = CppProcessor(filename, file_extension, error, verbosity)
     2907    processor.process(lines)
     2908
  • trunk/WebKitTools/Scripts/webkitpy/style/cpp_style_unittest.py

    r53636 r53675  
    55# Copyright (C) 2009 Torch Mobile Inc.
    66# Copyright (C) 2009 Apple Inc. All rights reserved.
     7# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
    78#
    89# Redistribution and use in source and binary forms, with or without
     
    4243import unittest
    4344import cpp_style
     45from cpp_style import CppProcessor
     46
    4447# FIXME: Remove the need to import anything from checker. See the
    4548#        FIXME notes near the STYLE_CATEGORIES definition for a
     
    36073610
    36083611
    3609     def test_can_handle(self):
    3610         """Tests for cpp_style.can_handle()."""
    3611         self.assert_(not cpp_style.can_handle(''))
    3612         self.assert_(cpp_style.can_handle('foo.h'))
    3613         self.assert_(not cpp_style.can_handle('foo.hpp'))
    3614         self.assert_(cpp_style.can_handle('foo.c'))
    3615         self.assert_(cpp_style.can_handle('foo.cpp'))
    3616         self.assert_(not cpp_style.can_handle('foo.cc'))
    3617         self.assert_(not cpp_style.can_handle('foo.cxx'))
    3618         self.assert_(not cpp_style.can_handle('foo.C'))
    3619         self.assert_(not cpp_style.can_handle('foo.mm'))
    3620         self.assert_(not cpp_style.can_handle('-'))
    3621 
    3622     def test_is_exempt(self):
    3623         """Tests for cpp_style.is_exempt()."""
    3624         self.assert_(not cpp_style.is_exempt(''))
    3625         self.assert_(not cpp_style.is_exempt('foo.h'))
    3626         self.assert_(not cpp_style.is_exempt('foo.hpp'))
    3627         self.assert_(not cpp_style.is_exempt('foo.c'))
    3628         self.assert_(not cpp_style.is_exempt('foo.cpp'))
    3629         self.assert_(not cpp_style.is_exempt('-'))
    3630         self.assert_(cpp_style.is_exempt('gtk2drawing.h'))
    3631         self.assert_(cpp_style.is_exempt('WebCore/platform/gtk/gtk2drawing.h'))
    3632         self.assert_(cpp_style.is_exempt('gtk2drawing.c'))
    3633         self.assert_(cpp_style.is_exempt('WebCore/platform/gtk/gtk2drawing.c'))
    3634         self.assert_(cpp_style.is_exempt('WebKit/qt/Api/qwebpage.h'))
    3635         self.assert_(cpp_style.is_exempt('JavaScriptCore/qt/api/qscriptengine_p.h'))
    3636         self.assert_(cpp_style.is_exempt('WebKit/qt/tests/qwebsecurityorigin/tst_qwebsecurityorigin.cpp'))
    3637         self.assert_(cpp_style.is_exempt('WebKit/gtk/tests/testatk.c'))
     3612class CppProcessorTest(unittest.TestCase):
     3613
     3614    """Tests CppProcessor class."""
     3615
     3616    def mock_handle_style_error(self):
     3617        pass
     3618
     3619    def _processor(self):
     3620        return CppProcessor("foo", "h", self.mock_handle_style_error, 3)
     3621
     3622    def test_init(self):
     3623        """Test __init__ constructor."""
     3624        processor = self._processor()
     3625        self.assertEquals(processor.file_extension, "h")
     3626        self.assertEquals(processor.file_path, "foo")
     3627        self.assertEquals(processor.handle_style_error, self.mock_handle_style_error)
     3628        self.assertEquals(processor.verbosity, 3)
     3629
     3630    def test_eq(self):
     3631        """Test __eq__ equality function."""
     3632        processor1 = self._processor()
     3633        processor2 = self._processor()
     3634
     3635        # == calls __eq__.
     3636        self.assertTrue(processor1 == processor2)
     3637
     3638        def mock_handle_style_error2(self):
     3639            pass
     3640
     3641        # Verify that a difference in any argument cause equality to fail.
     3642        processor = CppProcessor("foo", "h", self.mock_handle_style_error, 3)
     3643        self.assertFalse(processor == CppProcessor("bar", "h", self.mock_handle_style_error, 3))
     3644        self.assertFalse(processor == CppProcessor("foo", "c", self.mock_handle_style_error, 3))
     3645        self.assertFalse(processor == CppProcessor("foo", "h", mock_handle_style_error2, 3))
     3646        self.assertFalse(processor == CppProcessor("foo", "h", self.mock_handle_style_error, 4))
     3647
     3648    def test_ne(self):
     3649        """Test __ne__ inequality function."""
     3650        processor1 = self._processor()
     3651        processor2 = self._processor()
     3652
     3653        # != calls __ne__.
     3654        # By default, __ne__ always returns true on different objects.
     3655        # Thus, just check the distinguishing case to verify that the
     3656        # code defines __ne__.
     3657        self.assertFalse(processor1 != processor2)
     3658
    36383659
    36393660def tearDown():
  • trunk/WebKitTools/Scripts/webkitpy/style/text_style.py

    r53251 r53675  
    11# Copyright (C) 2009 Google Inc. All rights reserved.
     2# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
    23#
    34# Redistribution and use in source and binary forms, with or without
     
    2728# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    2829
    29 """Does WebKit-lint on text files.
    30 
    31 This module shares error count, filter setting, output setting, etc. with cpp_style.
    32 """
    33 
    34 import codecs
    35 import os.path
    36 import sys
    37 
    38 import cpp_style
     30"""Checks WebKit style for text files."""
    3931
    4032
    41 def process_file_data(filename, lines, error):
    42     """Performs lint check for text on the specified lines.
     33class TextProcessor(object):
    4334
    44     It reports errors to the given error function.
    45     """
    46     lines = (['// adjust line numbers to make the first line 1.'] + lines)
     35    """Processes text lines for checking style."""
    4736
    48     # FIXME: share with cpp_style.check_style()
    49     for line_number, line in enumerate(lines):
    50         if '\t' in line:
    51             error(filename, line_number, 'whitespace/tab', 5, 'Line contains tab character.')
     37    def __init__(self, file_path, handle_style_error):
     38        self.file_path = file_path
     39        self.handle_style_error = handle_style_error
     40
     41    def process(self, lines):
     42        lines = (["// adjust line numbers to make the first line 1."] + lines)
     43
     44        # FIXME: share with cpp_style.
     45        for line_number, line in enumerate(lines):
     46            if "\t" in line:
     47                self.handle_style_error(self.file_path, line_number,
     48                                        "whitespace/tab", 5,
     49                                        "Line contains tab character.")
    5250
    5351
    54 def process_file(filename, error):
    55     """Performs lint check for text on a single file."""
    56     if (not can_handle(filename)):
    57         sys.stderr.write('Ignoring %s; not a supported file\n' % filename)
    58         return
     52# FIXME: Remove this function (requires refactoring unit tests).
     53def process_file_data(filename, lines, error):
     54    processor = TextProcessor(filename, error)
     55    processor.process(lines)
    5956
    60     # FIXME: share code with cpp_style.process_file().
    61     try:
    62         # Do not support for filename='-'. cpp_style handles it.
    63         lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
    64     except IOError:
    65         sys.stderr.write("Skipping input '%s': Can't open for reading\n" % filename)
    66         return
    67     process_file_data(filename, lines, error)
    68 
    69 
    70 def can_handle(filename):
    71     """Checks if this module supports the specified file type.
    72 
    73     Args:
    74       filename: A filename. It may contain directory names.
    75     """
    76     return ("ChangeLog" in filename
    77             or "WebKitTools/Scripts/" in filename
    78             or os.path.splitext(filename)[1] in ('.css', '.html', '.idl', '.js', '.mm', '.php', '.pm', '.py', '.txt')) and not "LayoutTests/" in filename
  • trunk/WebKitTools/Scripts/webkitpy/style/text_style_unittest.py

    r52906 r53675  
    3333
    3434import text_style
    35 
     35from text_style import TextProcessor
    3636
    3737class TextStyleTestCase(unittest.TestCase):
     
    7777
    7878
    79     def test_can_handle(self):
    80         """Tests for text_style.can_handle()."""
    81         self.assert_(not text_style.can_handle(''))
    82         self.assert_(not text_style.can_handle('-'))
    83         self.assert_(text_style.can_handle('ChangeLog'))
    84         self.assert_(text_style.can_handle('WebCore/ChangeLog'))
    85         self.assert_(text_style.can_handle('FooChangeLog.bak'))
    86         self.assert_(text_style.can_handle('WebKitTools/Scripts/check-webkit=style'))
    87         self.assert_(text_style.can_handle('WebKitTools/Scripts/modules/text_style.py'))
    88         self.assert_(not text_style.can_handle('WebKitTools/Scripts'))
     79class TextProcessorTest(unittest.TestCase):
    8980
    90         self.assert_(text_style.can_handle('foo.css'))
    91         self.assert_(text_style.can_handle('foo.html'))
    92         self.assert_(text_style.can_handle('foo.idl'))
    93         self.assert_(text_style.can_handle('foo.js'))
    94         self.assert_(text_style.can_handle('WebCore/inspector/front-end/inspector.js'))
    95         self.assert_(text_style.can_handle('foo.mm'))
    96         self.assert_(text_style.can_handle('foo.php'))
    97         self.assert_(text_style.can_handle('foo.pm'))
    98         self.assert_(text_style.can_handle('foo.py'))
    99         self.assert_(text_style.can_handle('foo.txt'))
    100         self.assert_(not text_style.can_handle('foo.c'))
    101         self.assert_(not text_style.can_handle('foo.c'))
    102         self.assert_(not text_style.can_handle('foo.c'))
    103         self.assert_(not text_style.can_handle('foo.png'))
    104         self.assert_(not text_style.can_handle('foo.c/bar.png'))
    105         self.assert_(not text_style.can_handle('WebKit/English.lproj/Localizable.strings'))
    106         self.assert_(not text_style.can_handle('Makefile'))
    107         self.assert_(not text_style.can_handle('WebCore/Android.mk'))
    108         self.assert_(not text_style.can_handle('LayoutTests/inspector/console-tests.js'))
     81    """Tests TextProcessor class."""
     82
     83    def mock_handle_style_error(self):
     84        pass
     85
     86    def test_init(self):
     87        """Test __init__ constructor."""
     88        processor = TextProcessor("foo.txt", self.mock_handle_style_error)
     89        self.assertEquals(processor.file_path, "foo.txt")
     90        self.assertEquals(processor.handle_style_error, self.mock_handle_style_error)
    10991
    11092
Note: See TracChangeset for help on using the changeset viewer.