Viewing file: test_legacy.py (14.06 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details.
""" Test cases for L{twisted.logger._legacy}. """
from time import time import logging as py_logging
from zope.interface.verify import verifyObject, BrokenMethodImplementation
from twisted.trial import unittest
from twisted.python import context from twisted.python import log as legacyLog from twisted.python.failure import Failure
from .._levels import LogLevel from .._observer import ILogObserver from .._format import formatEvent from .._legacy import LegacyLogObserverWrapper from .._legacy import publishToNewObserver
class LegacyLogObserverWrapperTests(unittest.TestCase): """ Tests for L{LegacyLogObserverWrapper}. """
def test_interface(self): """ L{LegacyLogObserverWrapper} is an L{ILogObserver}. """ legacyObserver = lambda e: None observer = LegacyLogObserverWrapper(legacyObserver) try: verifyObject(ILogObserver, observer) except BrokenMethodImplementation as e: self.fail(e)
def test_repr(self): """ L{LegacyLogObserverWrapper} returns the expected string. """ class LegacyObserver(object): def __repr__(self): return "<Legacy Observer>"
def __call__(self): return
observer = LegacyLogObserverWrapper(LegacyObserver())
self.assertEqual( repr(observer), "LegacyLogObserverWrapper(<Legacy Observer>)" )
def observe(self, event): """ Send an event to a wrapped legacy observer and capture the event as seen by that observer.
@param event: an event @type event: L{dict}
@return: the event as observed by the legacy wrapper """ events = []
legacyObserver = lambda e: events.append(e) observer = LegacyLogObserverWrapper(legacyObserver) observer(event) self.assertEqual(len(events), 1)
return events[0]
def forwardAndVerify(self, event): """ Send an event to a wrapped legacy observer and verify that its data is preserved.
@param event: an event @type event: L{dict}
@return: the event as observed by the legacy wrapper """ # Make sure keys that are expected by the logging system are present event.setdefault("log_time", time()) event.setdefault("log_system", "-") event.setdefault("log_level", LogLevel.info)
# Send a copy: don't mutate me, bro observed = self.observe(dict(event))
# Don't expect modifications for key, value in event.items(): self.assertIn(key, observed)
return observed
def test_forward(self): """ Basic forwarding: event keys as observed by a legacy observer are the same. """ self.forwardAndVerify(dict(foo=1, bar=2))
def test_time(self): """ The new-style C{"log_time"} key is copied to the old-style C{"time"} key. """ stamp = time() event = self.forwardAndVerify(dict(log_time=stamp)) self.assertEqual(event["time"], stamp)
def test_timeAlreadySet(self): """ The new-style C{"log_time"} key does not step on a pre-existing old-style C{"time"} key. """ stamp = time() event = self.forwardAndVerify(dict(log_time=stamp + 1, time=stamp)) self.assertEqual(event["time"], stamp)
def test_system(self): """ The new-style C{"log_system"} key is copied to the old-style C{"system"} key. """ event = self.forwardAndVerify(dict(log_system="foo")) self.assertEqual(event["system"], "foo")
def test_systemAlreadySet(self): """ The new-style C{"log_system"} key does not step on a pre-existing old-style C{"system"} key. """ event = self.forwardAndVerify(dict(log_system="foo", system="bar")) self.assertEqual(event["system"], "bar")
def test_noSystem(self): """ If the new-style C{"log_system"} key is absent, the old-style C{"system"} key is set to C{"-"}. """ # Don't use forwardAndVerify(), since that sets log_system. event = dict(log_time=time(), log_level=LogLevel.info) observed = self.observe(dict(event)) self.assertEqual(observed["system"], "-")
def test_levelNotChange(self): """ If explicitly set, the C{isError} key will be preserved when forwarding from a new-style logging emitter to a legacy logging observer, regardless of log level. """ self.forwardAndVerify(dict(log_level=LogLevel.info, isError=1)) self.forwardAndVerify(dict(log_level=LogLevel.warn, isError=1)) self.forwardAndVerify(dict(log_level=LogLevel.error, isError=0)) self.forwardAndVerify(dict(log_level=LogLevel.critical, isError=0))
def test_pythonLogLevelNotSet(self): """ The new-style C{"log_level"} key is not translated to the old-style C{"logLevel"} key.
Events are forwarded from the old module from to new module and are then seen by old-style observers. We don't want to add unexpected keys to old-style events. """ event = self.forwardAndVerify(dict(log_level=LogLevel.info)) self.assertNotIn("logLevel", event)
def test_stringPythonLogLevel(self): """ If a stdlib log level was provided as a string (eg. C{"WARNING"}) in the legacy "logLevel" key, it does not get converted to a number. The documentation suggested that numerical values should be used but this was not a requirement. """ event = self.forwardAndVerify(dict( logLevel="WARNING", # py_logging.WARNING is 30 )) self.assertEqual(event["logLevel"], "WARNING")
def test_message(self): """ The old-style C{"message"} key is added, even if no new-style C{"log_format"} is given, as it is required, but may be empty. """ event = self.forwardAndVerify(dict()) self.assertEqual(event["message"], ()) # "message" is a tuple
def test_messageAlreadySet(self): """ The old-style C{"message"} key is not modified if it already exists. """ event = self.forwardAndVerify(dict(message=("foo", "bar"))) self.assertEqual(event["message"], ("foo", "bar"))
def test_format(self): """ Formatting is translated such that text is rendered correctly, even though old-style logging doesn't use PEP 3101 formatting. """ event = self.forwardAndVerify( dict(log_format="Hello, {who}!", who="world") ) self.assertEqual( legacyLog.textFromEventDict(event), "Hello, world!" )
def test_formatMessage(self): """ Using the message key, which is special in old-style, works for new-style formatting. """ event = self.forwardAndVerify( dict(log_format="Hello, {message}!", message="world") ) self.assertEqual( legacyLog.textFromEventDict(event), "Hello, world!" )
def test_formatAlreadySet(self): """ Formatting is not altered if the old-style C{"format"} key already exists. """ event = self.forwardAndVerify( dict(log_format="Hello!", format="Howdy!") ) self.assertEqual(legacyLog.textFromEventDict(event), "Howdy!")
def eventWithFailure(self, **values): """ Create a new-style event with a captured failure.
@param values: Additional values to include in the event. @type values: L{dict}
@return: the new event @rtype: L{dict} """ failure = Failure(RuntimeError("nyargh!")) return self.forwardAndVerify(dict( log_failure=failure, log_format="oopsie...", **values ))
def test_failure(self): """ Captured failures in the new style set the old-style C{"failure"}, C{"isError"}, and C{"why"} keys. """ event = self.eventWithFailure() self.assertIs(event["failure"], event["log_failure"]) self.assertTrue(event["isError"]) self.assertEqual(event["why"], "oopsie...")
def test_failureAlreadySet(self): """ Captured failures in the new style do not step on a pre-existing old-style C{"failure"} key. """ failure = Failure(RuntimeError("Weak salsa!")) event = self.eventWithFailure(failure=failure) self.assertIs(event["failure"], failure)
def test_isErrorAlreadySet(self): """ Captured failures in the new style do not step on a pre-existing old-style C{"isError"} key. """ event = self.eventWithFailure(isError=0) self.assertEqual(event["isError"], 0)
def test_whyAlreadySet(self): """ Captured failures in the new style do not step on a pre-existing old-style C{"failure"} key. """ event = self.eventWithFailure(why="blah") self.assertEqual(event["why"], "blah")
class PublishToNewObserverTests(unittest.TestCase): """ Tests for L{publishToNewObserver}. """
def setUp(self): self.events = [] self.observer = self.events.append
def legacyEvent(self, *message, **values): """ Return a basic old-style event as would be created by L{legacyLog.msg}.
@param message: a message event value in the legacy event format @type message: L{tuple} of L{bytes}
@param values: additional event values in the legacy event format @type event: L{dict}
@return: a legacy event """ event = (context.get(legacyLog.ILogContext) or {}).copy() event.update(values) event["message"] = message event["time"] = time() if "isError" not in event: event["isError"] = 0 return event
def test_observed(self): """ The observer is called exactly once. """ publishToNewObserver( self.observer, self.legacyEvent(), legacyLog.textFromEventDict ) self.assertEqual(len(self.events), 1)
def test_time(self): """ The old-style C{"time"} key is copied to the new-style C{"log_time"} key. """ publishToNewObserver( self.observer, self.legacyEvent(), legacyLog.textFromEventDict ) self.assertEqual( self.events[0]["log_time"], self.events[0]["time"] )
def test_message(self): """ A published old-style event should format as text in the same way as the given C{textFromEventDict} callable would format it. """ def textFromEventDict(event): return "".join(reversed(" ".join(event["message"])))
event = self.legacyEvent("Hello,", "world!") text = textFromEventDict(event)
publishToNewObserver(self.observer, event, textFromEventDict) self.assertEqual(formatEvent(self.events[0]), text)
def test_defaultLogLevel(self): """ Published event should have log level of L{LogLevel.info}. """ publishToNewObserver( self.observer, self.legacyEvent(), legacyLog.textFromEventDict ) self.assertEqual(self.events[0]["log_level"], LogLevel.info)
def test_isError(self): """ If C{"isError"} is set to C{1} (true) on the legacy event, the C{"log_level"} key should get set to L{LogLevel.critical}. """ publishToNewObserver( self.observer, self.legacyEvent(isError=1), legacyLog.textFromEventDict ) self.assertEqual(self.events[0]["log_level"], LogLevel.critical)
def test_stdlibLogLevel(self): """ If the old-style C{"logLevel"} key is set to a standard library logging level, using a predefined (L{int}) constant, the new-style C{"log_level"} key should get set to the corresponding log level. """ publishToNewObserver( self.observer, self.legacyEvent(logLevel=py_logging.WARNING), legacyLog.textFromEventDict ) self.assertEqual(self.events[0]["log_level"], LogLevel.warn)
def test_stdlibLogLevelWithString(self): """ If the old-style C{"logLevel"} key is set to a standard library logging level, using a string value, the new-style C{"log_level"} key should get set to the corresponding log level. """ publishToNewObserver( self.observer, self.legacyEvent(logLevel="WARNING"), legacyLog.textFromEventDict ) self.assertEqual(self.events[0]["log_level"], LogLevel.warn)
def test_stdlibLogLevelWithGarbage(self): """ If the old-style C{"logLevel"} key is set to a standard library logging level, using an unknown value, the new-style C{"log_level"} key should not get set. """ publishToNewObserver( self.observer, self.legacyEvent(logLevel="Foo!!!!!"), legacyLog.textFromEventDict ) self.assertNotIn("log_level", self.events[0])
def test_defaultNamespace(self): """ Published event should have a namespace of C{"log_legacy"} to indicate that it was forwarded from legacy logging. """ publishToNewObserver( self.observer, self.legacyEvent(), legacyLog.textFromEventDict ) self.assertEqual(self.events[0]["log_namespace"], "log_legacy")
def test_system(self): """ The old-style C{"system"} key is copied to the new-style C{"log_system"} key. """ publishToNewObserver( self.observer, self.legacyEvent(), legacyLog.textFromEventDict ) self.assertEqual( self.events[0]["log_system"], self.events[0]["system"] )
|