| Differences between
and this patch
- a/WebKitTools/ChangeLog +41 lines
Lines 1-3 a/WebKitTools/ChangeLog_sec1
1
2010-03-02  Chris Jerdonek  <cjerdonek@webkit.org>
2
3
        Reviewed by NOBODY (OOPS!).
4
5
        Started using the logging module in check-webkit-style.
6
        This provides more options for debugging and a more flexible,
7
        uniform way to report messages to the end-user.
8
9
        https://bugs.webkit.org/show_bug.cgi?id=35484
10
11
        Also included classes in a central location to facilitate
12
        the unit testing of logging code (setUp and tearDown of unit
13
        test logging configurations, etc).
14
15
        * Scripts/check-webkit-style:
16
          - Added a call to configure_logging() in the beginning of main().
17
          - Replaced two calls to sys.stderr.write() with appropriate
18
            logging calls.
19
20
        * Scripts/webkitpy/init/logtesting.py: Added.
21
          - Added a UnitTestLogStream class to capture log output
22
            during unit tests.
23
          - Added a UnitTestLog class that provides convenience methods
24
            for unit-testing logging code.
25
26
        * Scripts/webkitpy/style/checker.py:
27
          - Added a configure_logging() method.
28
          - Added a _LevelLoggingFilter class to filter out log messages
29
            above a certain logging level.
30
          - Removed the _stderr_write() method from the StyleChecker class
31
            and replaced its use with appropriate logging calls.
32
33
        * Scripts/webkitpy/style/checker_unittest.py:
34
          - Added a ConfigureLoggingTest class to unit test the
35
            configure_logging() method.
36
          - Updated the StyleCheckerCheckFileTest class as necessary.
37
38
        * Scripts/webkitpy/style_references.py:
39
          - Added references to logtesting.UnitTestLog and
40
            logtesting.UnitTestLogStream.
41
1
2010-03-01  Chris Fleizach  <cfleizach@apple.com>
42
2010-03-01  Chris Fleizach  <cfleizach@apple.com>
2
43
3
        Fixing broken DRT on Leopard/Tiger. Second try.
44
        Fixing broken DRT on Leopard/Tiger. Second try.
- a/WebKitTools/Scripts/check-webkit-style -7 / +24 lines
Lines 43-48 same line, but it is far from perfect (in either direction). a/WebKitTools/Scripts/check-webkit-style_sec1
43
"""
43
"""
44
44
45
import codecs
45
import codecs
46
import logging
46
import os
47
import os
47
import os.path
48
import os.path
48
import sys
49
import sys
Lines 50-62 import sys a/WebKitTools/Scripts/check-webkit-style_sec2
50
import webkitpy.style.checker as checker
51
import webkitpy.style.checker as checker
51
from webkitpy.style_references import SimpleScm
52
from webkitpy.style_references import SimpleScm
52
53
54
_log = logging.getLogger("check-webkit-style")
55
53
def main():
56
def main():
54
    # Change stderr to write with replacement characters so we don't die
57
    # Change stderr to write with replacement characters so we don't die
55
    # if we try to print something containing non-ASCII characters.
58
    # if we try to print something containing non-ASCII characters.
56
    sys.stderr = codecs.StreamReaderWriter(sys.stderr,
59
    stderr = codecs.StreamReaderWriter(sys.stderr,
57
                                           codecs.getreader('utf8'),
60
                                       codecs.getreader('utf8'),
58
                                           codecs.getwriter('utf8'),
61
                                       codecs.getwriter('utf8'),
59
                                           'replace')
62
                                       'replace')
63
    # Setting an "encoding" attribute on the stream is necessary to
64
    # prevent the logging module from raising an error.  See
65
    # the checker.configure_logging() function for more information.
66
    stderr.encoding = "UTF-8"
67
68
    # FIXME: Change webkitpy.style so that we do not need to overwrite
69
    #        the global sys.stderr.  This involves updating the code to
70
    #        accept a stream parameter where necessary, and not calling
71
    #        sys.stderr explicitly anywhere.
72
    sys.stderr = stderr
73
74
    checker.configure_logging(stderr)
75
60
    parser = checker.check_webkit_style_parser()
76
    parser = checker.check_webkit_style_parser()
61
    (files, options) = parser.parse(sys.argv[1:])
77
    (files, options) = parser.parse(sys.argv[1:])
62
78
Lines 78-84 def main(): a/WebKitTools/Scripts/check-webkit-style_sec3
78
                # FIXME: If the range is a "...", the code should find the common ancestor and
94
                # FIXME: If the range is a "...", the code should find the common ancestor and
79
                # start there (see git diff --help for information about how ... usually works).
95
                # start there (see git diff --help for information about how ... usually works).
80
                commit = commit[:commit.find('..')]
96
                commit = commit[:commit.find('..')]
81
                print >> sys.stderr, "Warning: Ranges are not supported for --git-commit. Checking all changes since %s.\n" % commit
97
                _log.warn("Ranges are not supported for --git-commit. "
98
                          "Checking all changes since %s." % commit)
82
            patch = scm.create_patch_since_local_commit(commit)
99
            patch = scm.create_patch_since_local_commit(commit)
83
        else:
100
        else:
84
            patch = scm.create_patch()
101
            patch = scm.create_patch()
Lines 86-93 def main(): a/WebKitTools/Scripts/check-webkit-style_sec4
86
103
87
    error_count = style_checker.error_count
104
    error_count = style_checker.error_count
88
    file_count = style_checker.file_count
105
    file_count = style_checker.file_count
89
    sys.stderr.write('Total errors found: %d in %d files\n'
106
    _log.info("Total errors found: %d in %d files"
90
                     % (error_count, file_count))
107
              % (error_count, file_count))
91
    # We fail when style errors are found or there are no checked files.
108
    # We fail when style errors are found or there are no checked files.
92
    sys.exit(error_count > 0 or file_count == 0)
109
    sys.exit(error_count > 0 or file_count == 0)
93
110
- a/WebKitTools/Scripts/webkitpy/init/logtesting.py +122 lines
Line 0 a/WebKitTools/Scripts/webkitpy/init/logtesting.py_sec1
1
# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
2
#
3
# Redistribution and use in source and binary forms, with or without
4
# modification, are permitted provided that the following conditions
5
# are met:
6
# 1.  Redistributions of source code must retain the above copyright
7
#     notice, this list of conditions and the following disclaimer.
8
# 2.  Redistributions in binary form must reproduce the above copyright
9
#     notice, this list of conditions and the following disclaimer in the
10
#     documentation and/or other materials provided with the distribution.
11
#
12
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
13
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
16
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
20
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
21
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23
"""Supports the unit-testing of logging."""
24
25
import logging
26
27
28
class UnitTestLogStream(object):
29
30
    """Represents a file-like object for unit-testing logging.
31
32
    This is meant for passing to the logging.StreamHandler constructor.
33
34
    """
35
36
    def __init__(self):
37
        self.messages = []
38
        """A list of log messages written to the stream."""
39
40
    def write(self, message):
41
        self.messages.append(message)
42
43
    def flush(self):
44
        pass
45
46
47
class UnitTestLog(object):
48
49
    """Supports unit-testing logging.
50
51
    Sample usage:
52
53
    # The following line should go in the setUp() method of a
54
    # unittest.TestCase.  In particular, self is a TestCase instance.
55
    self._log = UnitTestLog(self)
56
57
    # Call the following in a test method of a unittest.TestCase.
58
    log = logging.getLogger("webkitpy")
59
    log.info("message1")
60
    log.warn("message2")
61
    self._log.assertMessages(["INFO: message1\n",
62
                              "WARN: message2\n"])  # Should succeed.
63
64
    # The following line should go in the tearDown() method of
65
    # unittest.TestCase.
66
    self._log.tearDown()
67
68
    """
69
70
    def __init__(self, test_case, logging_level=None):
71
        """Configure unit test logging, and return an instance.
72
73
        This method configures the root logger to log to a testing
74
        log stream.  Only messages logged at or above the given level
75
        are logged to the stream.  Messages are formatted in the
76
        following way, for example--
77
78
        "INFO: This is a test log message."
79
80
        This method should normally be called in the setUp() method
81
        of a unittest.TestCase.
82
83
        Args:
84
          test_case: A unittest.TestCase instance.
85
          logging_level: A logging level.  Only messages logged at or
86
                         above this level are written to the testing
87
                         log stream.  Defaults to logging.INFO.
88
89
        """
90
        if logging_level is None:
91
            logging_level = logging.INFO
92
93
        stream = UnitTestLogStream()
94
        handler = logging.StreamHandler(stream)
95
        formatter = logging.Formatter("%(levelname)s: %(message)s")
96
        handler.setFormatter(formatter)
97
98
        logger = logging.getLogger()
99
        logger.setLevel(logging_level)
100
        logger.addHandler(handler)
101
102
        self._handler = handler
103
        self._messages = stream.messages
104
        self._test_case = test_case
105
106
    def _logger(self):
107
        """Return the root logger used for logging."""
108
        return logging.getLogger()
109
110
    def assertMessages(self, messages):
111
        """Assert that the given messages match the logged messages."""
112
        self._test_case.assertEquals(messages, self._messages)
113
114
    def tearDown(self):
115
        """Unconfigure unit test logging.
116
117
        This should normally be called in the tearDown method of a
118
        unittest.TestCase
119
120
        """
121
        logger = self._logger()
122
        logger.removeHandler(self._handler)
- a/WebKitTools/Scripts/webkitpy/style/checker.py -6 / +93 lines
Lines 30-35 a/WebKitTools/Scripts/webkitpy/style/checker.py_sec1
30
"""Front end of some style-checker modules."""
30
"""Front end of some style-checker modules."""
31
31
32
import codecs
32
import codecs
33
import logging
33
import os.path
34
import os.path
34
import sys
35
import sys
35
36
Lines 44-49 from processors.common import categories as CommonCategories a/WebKitTools/Scripts/webkitpy/style/checker.py_sec2
44
from processors.cpp import CppProcessor
45
from processors.cpp import CppProcessor
45
from processors.text import TextProcessor
46
from processors.text import TextProcessor
46
47
48
_log = logging.getLogger("webkitpy.style.checker")
47
49
48
# These are default option values for the command-line option parser.
50
# These are default option values for the command-line option parser.
49
_DEFAULT_VERBOSITY = 1
51
_DEFAULT_VERBOSITY = 1
Lines 224-229 def check_webkit_style_configuration(options): a/WebKitTools/Scripts/webkitpy/style/checker.py_sec3
224
               verbosity=options.verbosity)
226
               verbosity=options.verbosity)
225
227
226
228
229
# FIXME: Add support for more verbose logging for debug purposes.
230
#        This can use a formatter like the following, for example--
231
#
232
#        formatter = logging.Formatter("%(name)s: [%(levelname)s] %(message)s")
233
def configure_logging(stream):
234
    """Configure logging, and return the list of handlers added.
235
236
    Configures the root logger to log INFO messages and higher.
237
    Formats WARNING messages and above to display the logging level
238
    and messages strictly below WARNING not to display it.
239
240
    Returns:
241
      A list of references to the logging handlers added to the root
242
      logger.  This allows the caller to later remove the handlers
243
      using logger.removeHandler.  This is useful primarily during unit
244
      testing where the caller may want to configure logging temporarily
245
      and then undo the configuring.
246
247
    Args:
248
      stream: A file-like object to which to log.  The stream must
249
              define an "encoding" data attribute, or else logging
250
              raises an error.
251
252
    """
253
    # If the stream does not define an "encoding" data attribute, the
254
    # logging module can throw an error like the following:
255
    #
256
    # Traceback (most recent call last):
257
    #   File "/System/Library/Frameworks/Python.framework/Versions/2.6/...
258
    #         lib/python2.6/logging/__init__.py", line 761, in emit
259
    #     self.stream.write(fs % msg.encode(self.stream.encoding))
260
    # LookupError: unknown encoding: unknown
261
262
    # Handles logging.WARNING and above.
263
    error_handler = logging.StreamHandler(stream)
264
    error_handler.setLevel(logging.WARNING)
265
    formatter = logging.Formatter("%(levelname)s: %(message)s")
266
    error_handler.setFormatter(formatter)
267
268
    # Handles records strictly below logging.WARNING.
269
    non_error_handler = logging.StreamHandler(stream)
270
    non_error_filter = _LevelLoggingFilter(logging.WARNING)
271
    non_error_handler.addFilter(non_error_filter)
272
    formatter = logging.Formatter("%(message)s")
273
    non_error_handler.setFormatter(formatter)
274
275
    logger = logging.getLogger()
276
    logger.setLevel(logging.INFO)
277
278
    handlers = [error_handler, non_error_handler]
279
280
    for handler in handlers:
281
        logger.addHandler(handler)
282
283
    return handlers
284
285
286
# FIXME: Consider moving this class into a module in webkitpy.init after
287
#        getting more experience with its use.  We want to make sure
288
#        we have the right API before doing so.  For example, we may
289
#        want to provide a constructor that has both upper and lower
290
#        bounds, and not just an upper bound.
291
class _LevelLoggingFilter(object):
292
293
    """A logging filter for blocking records at or above a certain level."""
294
295
    def __init__(self, logging_level):
296
        """Create a _LevelLoggingFilter.
297
298
        Args:
299
          logging_level: The logging level cut-off.  Logging levels at
300
                         or above this level will not be logged.
301
302
        """
303
        self._logging_level = logging_level
304
305
    # The logging module requires that this method be defined.
306
    def filter(self, log_record):
307
        """Return whether given the LogRecord should be logged."""
308
        return log_record.levelno < self._logging_level
309
310
227
# Enum-like idiom
311
# Enum-like idiom
228
class FileType:
312
class FileType:
229
313
Lines 322-327 class ProcessorDispatcher(object): a/WebKitTools/Scripts/webkitpy/style/checker.py_sec4
322
        return processor
406
        return processor
323
407
324
408
409
# FIXME: Remove the stderr_write attribute from this class and replace
410
#        its use with calls to a logging module logger.
325
class StyleCheckerConfiguration(object):
411
class StyleCheckerConfiguration(object):
326
412
327
    """Stores configuration values for the StyleChecker class.
413
    """Stores configuration values for the StyleChecker class.
Lines 439-447 class StyleChecker(object): a/WebKitTools/Scripts/webkitpy/style/checker.py_sec5
439
        self.error_count = 0
525
        self.error_count = 0
440
        self.file_count = 0
526
        self.file_count = 0
441
527
442
    def _stderr_write(self, message):
443
        self._configuration.stderr_write(message)
444
445
    def _increment_error_count(self):
528
    def _increment_error_count(self):
446
        """Increment the total count of reported errors."""
529
        """Increment the total count of reported errors."""
447
        self.error_count += 1
530
        self.error_count += 1
Lines 469-479 class StyleChecker(object): a/WebKitTools/Scripts/webkitpy/style/checker.py_sec6
469
            contents = file.read()
552
            contents = file.read()
470
553
471
        except IOError:
554
        except IOError:
472
            self._stderr_write("Skipping input '%s': Can't open for reading\n" % file_path)
555
            message = 'Could not read file. Skipping: "%s"' % file_path
556
            _log.warn(message)
473
            return
557
            return
474
558
475
        lines = contents.split("\n")
559
        lines = contents.split("\n")
476
560
561
        # FIXME: Make a CarriageReturnProcessor for this logic, and put
562
        #        it in processors.common.  The process() method should
563
        #        return the lines with the carriage returns stripped.
477
        for line_number in range(len(lines)):
564
        for line_number in range(len(lines)):
478
            # FIXME: We should probably use the SVN "eol-style" property
565
            # FIXME: We should probably use the SVN "eol-style" property
479
            #        or a white list to decide whether or not to do
566
            #        or a white list to decide whether or not to do
Lines 520-527 class StyleChecker(object): a/WebKitTools/Scripts/webkitpy/style/checker.py_sec7
520
        if dispatcher.should_skip_without_warning(file_path):
607
        if dispatcher.should_skip_without_warning(file_path):
521
            return
608
            return
522
        if dispatcher.should_skip_with_warning(file_path):
609
        if dispatcher.should_skip_with_warning(file_path):
523
            self._stderr_write('Ignoring "%s": this file is exempt from the '
610
            _log.warn('File exempt from style guide. Skipping: "%s"'
524
                               "style guide.\n" % file_path)
611
                      % file_path)
525
            return
612
            return
526
613
527
        verbosity = self._configuration.verbosity
614
        verbosity = self._configuration.verbosity
- a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py -2 / +63 lines
Lines 34-48 a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py_sec1
34
34
35
"""Unit tests for style.py."""
35
"""Unit tests for style.py."""
36
36
37
import logging
37
import unittest
38
import unittest
38
39
39
import checker as style
40
import checker as style
41
from ..style_references import UnitTestLog
42
from ..style_references import UnitTestLogStream
40
from checker import _BASE_FILTER_RULES
43
from checker import _BASE_FILTER_RULES
41
from checker import _MAX_REPORTS_PER_CATEGORY
44
from checker import _MAX_REPORTS_PER_CATEGORY
42
from checker import _PATH_RULES_SPECIFIER as PATH_RULES_SPECIFIER
45
from checker import _PATH_RULES_SPECIFIER as PATH_RULES_SPECIFIER
43
from checker import _all_categories
46
from checker import _all_categories
44
from checker import check_webkit_style_configuration
47
from checker import check_webkit_style_configuration
45
from checker import check_webkit_style_parser
48
from checker import check_webkit_style_parser
49
from checker import configure_logging
46
from checker import ProcessorDispatcher
50
from checker import ProcessorDispatcher
47
from checker import StyleChecker
51
from checker import StyleChecker
48
from checker import StyleCheckerConfiguration
52
from checker import StyleCheckerConfiguration
Lines 54-59 from processors.cpp import CppProcessor a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py_sec2
54
from processors.text import TextProcessor
58
from processors.text import TextProcessor
55
59
56
60
61
class ConfigureLoggingTest(unittest.TestCase):
62
63
    """Tests the configure_logging() function."""
64
65
    def setUp(self):
66
        log_stream = UnitTestLogStream()
67
68
        self._handlers = configure_logging(log_stream)
69
        self._log_stream = log_stream
70
71
    def tearDown(self):
72
        """Reset logging to its original state.
73
74
        This method ensures that the logging configuration set up
75
        for a unit test does not affect logging in other unit tests.
76
77
        """
78
        # This should be the same as the logger configured in the
79
        # configure_logging() method.
80
        logger = logging.getLogger()
81
        for handler in self._handlers:
82
            logger.removeHandler(handler)
83
84
    def _log(self):
85
        return logging.getLogger("webkitpy")
86
87
    def assert_log_messages(self, messages):
88
        """Assert that the logged messages equal the given messages."""
89
        self.assertEquals(messages, self._log_stream.messages)
90
91
    def test_warning_message(self):
92
        self._log().warn("test message")
93
        self.assert_log_messages(["WARNING: test message\n"])
94
95
    def test_below_warning_message(self):
96
        # We test the boundary case of a logging level equal to 29.
97
        # In practice, we will probably only be calling log.info(),
98
        # which corresponds to a logging level of 20.
99
        level = logging.WARNING - 1  # Equals 29.
100
        self._log().log(level, "test message")
101
        self.assert_log_messages(["test message\n"])
102
103
    def test_debug_message(self):
104
        self._log().debug("test message")
105
        self.assert_log_messages([])
106
107
    def test_two_messages(self):
108
        self._log().info("message1")
109
        self._log().info("message2")
110
        self.assert_log_messages(["message1\n", "message2\n"])
111
112
57
class GlobalVariablesTest(unittest.TestCase):
113
class GlobalVariablesTest(unittest.TestCase):
58
114
59
    """Tests validity of the global variables."""
115
    """Tests validity of the global variables."""
Lines 446-456 class StyleCheckerCheckFileTest(unittest.TestCase): a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py_sec3
446
502
447
    """
503
    """
448
    def setUp(self):
504
    def setUp(self):
505
        self._log = UnitTestLog(self)
449
        self.got_file_path = None
506
        self.got_file_path = None
450
        self.got_handle_style_error = None
507
        self.got_handle_style_error = None
451
        self.got_processor = None
508
        self.got_processor = None
452
        self.warning_messages = ""
509
        self.warning_messages = ""
453
510
511
    def tearDown(self):
512
        self._log.tearDown()
513
454
    def mock_stderr_write(self, warning_message):
514
    def mock_stderr_write(self, warning_message):
455
        self.warning_messages += warning_message
515
        self.warning_messages += warning_message
456
516
Lines 523-530 class StyleCheckerCheckFileTest(unittest.TestCase): a/WebKitTools/Scripts/webkitpy/style/checker_unittest.py_sec4
523
583
524
        # Check the outcome.
584
        # Check the outcome.
525
        self.call_check_file(file_path)
585
        self.call_check_file(file_path)
526
        self.assert_attributes(None, None, None,
586
        self.assert_attributes(None, None, None, "")
527
                               'Ignoring "gtk2drawing.c": this file is exempt from the style guide.\n')
587
        self._log.assertMessages(["WARNING: File exempt from style guide. "
588
                                  'Skipping: "gtk2drawing.c"\n'])
528
589
529
    def test_check_file_on_non_skipped(self):
590
    def test_check_file_on_non_skipped(self):
530
591
- a/WebKitTools/Scripts/webkitpy/style_references.py +2 lines
Lines 41-46 a/WebKitTools/Scripts/webkitpy/style_references.py_sec1
41
import os
41
import os
42
42
43
from diff_parser import DiffParser
43
from diff_parser import DiffParser
44
from init.logtesting import UnitTestLog
45
from init.logtesting import UnitTestLogStream
44
from scm import detect_scm_system
46
from scm import detect_scm_system
45
47
46
48

Return to Bug 35484