Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org) | 1 # Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org) |
| 2 # | 2 # |
| 3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
| 4 # modification, are permitted provided that the following conditions | 4 # modification, are permitted provided that the following conditions |
| 5 # are met: | 5 # are met: |
| 6 # 1. Redistributions of source code must retain the above copyright | 6 # 1. Redistributions of source code must retain the above copyright |
| 7 # notice, this list of conditions and the following disclaimer. | 7 # notice, this list of conditions and the following disclaimer. |
| 8 # 2. Redistributions in binary form must reproduce the above copyright | 8 # 2. Redistributions in binary form must reproduce the above copyright |
| 9 # notice, this list of conditions and the following disclaimer in the | 9 # notice, this list of conditions and the following disclaimer in the |
| 10 # documentation and/or other materials provided with the distribution. | 10 # documentation and/or other materials provided with the distribution. |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 21 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 21 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 22 | 22 |
| 23 """Supports the unit-testing of logging code. | 23 """Supports the unit-testing of logging code. |
| 24 | 24 |
| 25 Provides support for unit-testing messages logged using the built-in | 25 Provides support for unit-testing messages logged using the built-in |
| 26 logging module. | 26 logging module. |
| 27 | 27 |
| 28 Inherit from the LoggingTestCase class for basic testing needs. For | 28 Inherit from the LoggingTestCase class for basic testing needs. For |
| 29 more advanced needs (e.g. unit-testing methods that configure logging), | 29 more advanced needs (e.g. unit-testing methods that configure logging), |
| 30 see the TestLogStream class, and perhaps also the LogTesting class. | 30 see the TestLogStream class, and perhaps also the LogTesting class. |
| 31 | |
| 32 """ | 31 """ |
| 33 | 32 |
| 34 import logging | 33 import logging |
| 35 import unittest | 34 import unittest |
| 36 | 35 |
| 37 | 36 |
| 38 class TestLogStream(object): | 37 class TestLogStream(object): |
| 39 | 38 |
| 40 """Represents a file-like object for unit-testing logging. | 39 """Represents a file-like object for unit-testing logging. |
| 41 | 40 |
| 42 This is meant for passing to the logging.StreamHandler constructor. | 41 This is meant for passing to the logging.StreamHandler constructor. |
| 43 Log messages captured by instances of this object can be tested | 42 Log messages captured by instances of this object can be tested |
| 44 using self.assertMessages() below. | 43 using self.assertMessages() below. |
| 45 | |
| 46 """ | 44 """ |
| 47 | 45 |
| 48 def __init__(self, test_case): | 46 def __init__(self, test_case): |
| 49 """Create an instance. | 47 """Create an instance. |
| 50 | 48 |
| 51 Args: | 49 Args: |
| 52 test_case: A unittest.TestCase instance. | 50 test_case: A unittest.TestCase instance. |
| 53 | |
| 54 """ | 51 """ |
| 55 self._test_case = test_case | 52 self._test_case = test_case |
| 56 self.messages = [] | 53 self.messages = [] |
| 57 """A list of log messages written to the stream.""" | 54 """A list of log messages written to the stream.""" |
| 58 | 55 |
| 59 # Python documentation says that any object passed to the StreamHandler | 56 # Python documentation says that any object passed to the StreamHandler |
| 60 # constructor should support write() and flush(): | 57 # constructor should support write() and flush(): |
| 61 # | 58 # |
| 62 # http://docs.python.org/library/logging.html#module-logging.handlers | 59 # http://docs.python.org/library/logging.html#module-logging.handlers |
| 63 | 60 |
| 64 def write(self, message): | 61 def write(self, message): |
| 65 self.messages.append(message) | 62 self.messages.append(message) |
| 66 | 63 |
| 67 def flush(self): | 64 def flush(self): |
| 68 pass | 65 pass |
| 69 | 66 |
| 70 def assertMessages(self, messages): | 67 def assertMessages(self, messages): |
| 71 """Assert that the given messages match the logged messages. | 68 """Assert that the given messages match the logged messages. |
| 72 | 69 |
| 73 messages: A list of log message strings. | 70 messages: A list of log message strings. |
| 74 | |
| 75 """ | 71 """ |
| 76 self._test_case.assertEqual(messages, self.messages) | 72 self._test_case.assertEqual(messages, self.messages) |
| 77 | 73 |
| 78 | 74 |
| 79 class LogTesting(object): | 75 class LogTesting(object): |
| 80 | 76 |
| 81 """Supports end-to-end unit-testing of log messages. | 77 """Supports end-to-end unit-testing of log messages. |
| 82 | 78 |
| 83 Sample usage: | 79 Sample usage: |
| 84 | 80 |
| 85 class SampleTest(unittest.TestCase): | 81 class SampleTest(unittest.TestCase): |
| 86 | 82 |
| 87 def setUp(self): | 83 def setUp(self): |
| 88 self._log = LogTesting.setUp(self) # Turn logging on. | 84 self._log = LogTesting.setUp(self) # Turn logging on. |
| 89 | 85 |
| 90 def tearDown(self): | 86 def tearDown(self): |
| 91 self._log.tearDown() # Turn off and reset logging. | 87 self._log.tearDown() # Turn off and reset logging. |
| 92 | 88 |
| 93 def test_logging_in_some_method(self): | 89 def test_logging_in_some_method(self): |
| 94 call_some_method() # Contains calls to _log.info(), etc. | 90 call_some_method() # Contains calls to _log.info(), etc. |
| 95 | 91 |
| 96 # Check the resulting log messages. | 92 # Check the resulting log messages. |
| 97 self._log.assertMessages(["INFO: expected message #1", | 93 self._log.assertMessages(["INFO: expected message #1", |
| 98 "WARNING: expected message #2"]) | 94 "WARNING: expected message #2"]) |
| 99 | 95 """ |
|
Dirk Pranke
2016/09/06 01:14:27
This indentation seems wrong?
| |
| 100 """ | |
| 101 | 96 |
| 102 def __init__(self, test_stream, handler): | 97 def __init__(self, test_stream, handler): |
| 103 """Create an instance. | 98 """Create an instance. |
| 104 | 99 |
| 105 This method should never be called directly. Instances should | 100 This method should never be called directly. Instances should |
| 106 instead be created using the static setUp() method. | 101 instead be created using the static setUp() method. |
| 107 | 102 |
| 108 Args: | 103 Args: |
| 109 test_stream: A TestLogStream instance. | 104 test_stream: A TestLogStream instance. |
| 110 handler: The handler added to the logger. | 105 handler: The handler added to the logger. |
| 111 | |
| 112 """ | 106 """ |
| 113 self._test_stream = test_stream | 107 self._test_stream = test_stream |
| 114 self._handler = handler | 108 self._handler = handler |
| 115 | 109 |
| 116 @staticmethod | 110 @staticmethod |
| 117 def _getLogger(): | 111 def _getLogger(): |
| 118 """Return the logger being tested.""" | 112 """Return the logger being tested.""" |
| 119 # It is possible we might want to return something other than | 113 # It is possible we might want to return something other than |
| 120 # the root logger in some special situation. For now, the | 114 # the root logger in some special situation. For now, the |
| 121 # root logger seems to suffice. | 115 # root logger seems to suffice. |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 136 of a unittest.TestCase. See the docstring of this class | 130 of a unittest.TestCase. See the docstring of this class |
| 137 for more details. | 131 for more details. |
| 138 | 132 |
| 139 Returns: | 133 Returns: |
| 140 A LogTesting instance. | 134 A LogTesting instance. |
| 141 | 135 |
| 142 Args: | 136 Args: |
| 143 test_case: A unittest.TestCase instance. | 137 test_case: A unittest.TestCase instance. |
| 144 logging_level: An integer logging level that is the minimum level | 138 logging_level: An integer logging level that is the minimum level |
| 145 of log messages you would like to test. | 139 of log messages you would like to test. |
| 146 | |
| 147 """ | 140 """ |
| 148 stream = TestLogStream(test_case) | 141 stream = TestLogStream(test_case) |
| 149 handler = logging.StreamHandler(stream) | 142 handler = logging.StreamHandler(stream) |
| 150 handler.setLevel(logging_level) | 143 handler.setLevel(logging_level) |
| 151 formatter = logging.Formatter("%(levelname)s: %(message)s") | 144 formatter = logging.Formatter("%(levelname)s: %(message)s") |
| 152 handler.setFormatter(formatter) | 145 handler.setFormatter(formatter) |
| 153 | 146 |
| 154 # Notice that we only change the root logger by adding a handler | 147 # Notice that we only change the root logger by adding a handler |
| 155 # to it. In particular, we do not reset its level using | 148 # to it. In particular, we do not reset its level using |
| 156 # logger.setLevel(). This ensures that we have not interfered | 149 # logger.setLevel(). This ensures that we have not interfered |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 181 # | 174 # |
| 182 # The latter ensures that no extra log messages are getting logged that | 175 # The latter ensures that no extra log messages are getting logged that |
| 183 # the caller might not be aware of or may have forgotten to check for. | 176 # the caller might not be aware of or may have forgotten to check for. |
| 184 # This gets us a bit more mileage out of our tests without writing any | 177 # This gets us a bit more mileage out of our tests without writing any |
| 185 # additional code. | 178 # additional code. |
| 186 def assertMessages(self, messages): | 179 def assertMessages(self, messages): |
| 187 """Assert the current array of log messages, and clear its contents. | 180 """Assert the current array of log messages, and clear its contents. |
| 188 | 181 |
| 189 Args: | 182 Args: |
| 190 messages: A list of log message strings. | 183 messages: A list of log message strings. |
| 191 | |
| 192 """ | 184 """ |
| 193 try: | 185 try: |
| 194 self._test_stream.assertMessages(messages) | 186 self._test_stream.assertMessages(messages) |
| 195 finally: | 187 finally: |
| 196 # We want to clear the array of messages even in the case of | 188 # We want to clear the array of messages even in the case of |
| 197 # an Exception (e.g. an AssertionError). Otherwise, another | 189 # an Exception (e.g. an AssertionError). Otherwise, another |
| 198 # AssertionError can occur in the tearDown() because the | 190 # AssertionError can occur in the tearDown() because the |
| 199 # array might not have gotten emptied. | 191 # array might not have gotten emptied. |
| 200 self._test_stream.messages = [] | 192 self._test_stream.messages = [] |
| 201 | 193 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 215 Sample usage: | 207 Sample usage: |
| 216 | 208 |
| 217 class SampleTest(LoggingTestCase): | 209 class SampleTest(LoggingTestCase): |
| 218 | 210 |
| 219 def test_logging_in_some_method(self): | 211 def test_logging_in_some_method(self): |
| 220 call_some_method() # Contains calls to _log.info(), etc. | 212 call_some_method() # Contains calls to _log.info(), etc. |
| 221 | 213 |
| 222 # Check the resulting log messages. | 214 # Check the resulting log messages. |
| 223 self.assertLog(["INFO: expected message #1", | 215 self.assertLog(["INFO: expected message #1", |
| 224 "WARNING: expected message #2"]) | 216 "WARNING: expected message #2"]) |
| 225 | 217 """ |
| 226 """ | |
| 227 | 218 |
| 228 def setUp(self): | 219 def setUp(self): |
| 229 self._log = LogTesting.setUp(self) | 220 self._log = LogTesting.setUp(self) |
| 230 | 221 |
| 231 def tearDown(self): | 222 def tearDown(self): |
| 232 self._log.tearDown() | 223 self._log.tearDown() |
| 233 | 224 |
| 234 def logMessages(self): | 225 def logMessages(self): |
| 235 """Return the current list of log messages.""" | 226 """Return the current list of log messages.""" |
| 236 return self._log.messages() | 227 return self._log.messages() |
| 237 | 228 |
| 238 # FIXME: Add a clearMessages() method for cases where the caller | 229 # FIXME: Add a clearMessages() method for cases where the caller |
| 239 # deliberately doesn't want to assert every message. | 230 # deliberately doesn't want to assert every message. |
| 240 | 231 |
| 241 # See the code comments preceding LogTesting.assertMessages() for | 232 # See the code comments preceding LogTesting.assertMessages() for |
| 242 # an explanation of why we clear the array of messages after | 233 # an explanation of why we clear the array of messages after |
| 243 # asserting its contents. | 234 # asserting its contents. |
| 244 def assertLog(self, messages): | 235 def assertLog(self, messages): |
| 245 """Assert the current array of log messages, and clear its contents. | 236 """Assert the current array of log messages, and clear its contents. |
| 246 | 237 |
| 247 Args: | 238 Args: |
| 248 messages: A list of log message strings. | 239 messages: A list of log message strings. |
| 249 | |
| 250 """ | 240 """ |
| 251 self._log.assertMessages(messages) | 241 self._log.assertMessages(messages) |
| OLD | NEW |