RAHHH
This commit is contained in:
@@ -0,0 +1,289 @@
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Test cases for L{twisted.logger._io}.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from typing import List, Optional
|
||||
|
||||
from zope.interface import implementer
|
||||
|
||||
from constantly import NamedConstant
|
||||
|
||||
from twisted.trial import unittest
|
||||
from .._interfaces import ILogObserver, LogEvent
|
||||
from .._io import LoggingFile
|
||||
from .._levels import LogLevel
|
||||
from .._logger import Logger
|
||||
from .._observer import LogPublisher
|
||||
|
||||
|
||||
@implementer(ILogObserver)
|
||||
class TestLoggingFile(LoggingFile):
|
||||
"""
|
||||
L{LoggingFile} that is also an observer which captures events and messages.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
logger: Logger,
|
||||
level: NamedConstant = LogLevel.info,
|
||||
encoding: Optional[str] = None,
|
||||
) -> None:
|
||||
super().__init__(logger=logger, level=level, encoding=encoding)
|
||||
self.events: List[LogEvent] = []
|
||||
self.messages: List[str] = []
|
||||
|
||||
def __call__(self, event: LogEvent) -> None:
|
||||
self.events.append(event)
|
||||
if "log_io" in event:
|
||||
self.messages.append(event["log_io"])
|
||||
|
||||
|
||||
class LoggingFileTests(unittest.TestCase):
|
||||
"""
|
||||
Tests for L{LoggingFile}.
|
||||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""
|
||||
Create a logger for test L{LoggingFile} instances to use.
|
||||
"""
|
||||
self.publisher = LogPublisher()
|
||||
self.logger = Logger(observer=self.publisher)
|
||||
|
||||
def test_softspace(self) -> None:
|
||||
"""
|
||||
L{LoggingFile.softspace} is 0.
|
||||
"""
|
||||
self.assertEqual(LoggingFile(self.logger).softspace, 0)
|
||||
|
||||
warningsShown = self.flushWarnings([self.test_softspace])
|
||||
self.assertEqual(len(warningsShown), 1)
|
||||
self.assertEqual(warningsShown[0]["category"], DeprecationWarning)
|
||||
deprecatedClass = "twisted.logger._io.LoggingFile.softspace"
|
||||
self.assertEqual(
|
||||
warningsShown[0]["message"],
|
||||
"%s was deprecated in Twisted 21.2.0" % (deprecatedClass),
|
||||
)
|
||||
|
||||
def test_readOnlyAttributes(self) -> None:
|
||||
"""
|
||||
Some L{LoggingFile} attributes are read-only.
|
||||
"""
|
||||
f = LoggingFile(self.logger)
|
||||
|
||||
self.assertRaises(AttributeError, setattr, f, "closed", True)
|
||||
self.assertRaises(AttributeError, setattr, f, "encoding", "utf-8")
|
||||
self.assertRaises(AttributeError, setattr, f, "mode", "r")
|
||||
self.assertRaises(AttributeError, setattr, f, "newlines", ["\n"])
|
||||
self.assertRaises(AttributeError, setattr, f, "name", "foo")
|
||||
|
||||
def test_unsupportedMethods(self) -> None:
|
||||
"""
|
||||
Some L{LoggingFile} methods are unsupported.
|
||||
"""
|
||||
f = LoggingFile(self.logger)
|
||||
|
||||
self.assertRaises(IOError, f.read)
|
||||
self.assertRaises(IOError, f.next)
|
||||
self.assertRaises(IOError, f.readline)
|
||||
self.assertRaises(IOError, f.readlines)
|
||||
self.assertRaises(IOError, f.xreadlines)
|
||||
self.assertRaises(IOError, f.seek)
|
||||
self.assertRaises(IOError, f.tell)
|
||||
self.assertRaises(IOError, f.truncate)
|
||||
|
||||
def test_level(self) -> None:
|
||||
"""
|
||||
Default level is L{LogLevel.info} if not set.
|
||||
"""
|
||||
f = LoggingFile(self.logger)
|
||||
self.assertEqual(f.level, LogLevel.info)
|
||||
|
||||
f = LoggingFile(self.logger, level=LogLevel.error)
|
||||
self.assertEqual(f.level, LogLevel.error)
|
||||
|
||||
def test_encoding(self) -> None:
|
||||
"""
|
||||
Default encoding is C{sys.getdefaultencoding()} if not set.
|
||||
"""
|
||||
f = LoggingFile(self.logger)
|
||||
self.assertEqual(f.encoding, sys.getdefaultencoding())
|
||||
|
||||
f = LoggingFile(self.logger, encoding="utf-8")
|
||||
self.assertEqual(f.encoding, "utf-8")
|
||||
|
||||
def test_mode(self) -> None:
|
||||
"""
|
||||
Reported mode is C{"w"}.
|
||||
"""
|
||||
f = LoggingFile(self.logger)
|
||||
self.assertEqual(f.mode, "w")
|
||||
|
||||
def test_newlines(self) -> None:
|
||||
"""
|
||||
The C{newlines} attribute is L{None}.
|
||||
"""
|
||||
f = LoggingFile(self.logger)
|
||||
self.assertIsNone(f.newlines)
|
||||
|
||||
def test_name(self) -> None:
|
||||
"""
|
||||
The C{name} attribute is fixed.
|
||||
"""
|
||||
f = LoggingFile(self.logger)
|
||||
self.assertEqual(f.name, "<LoggingFile twisted.logger.test.test_io#info>")
|
||||
|
||||
def test_close(self) -> None:
|
||||
"""
|
||||
L{LoggingFile.close} closes the file.
|
||||
"""
|
||||
f = LoggingFile(self.logger)
|
||||
f.close()
|
||||
|
||||
self.assertTrue(f.closed)
|
||||
self.assertRaises(ValueError, f.write, "Hello")
|
||||
|
||||
def test_flush(self) -> None:
|
||||
"""
|
||||
L{LoggingFile.flush} does nothing.
|
||||
"""
|
||||
f = LoggingFile(self.logger)
|
||||
f.flush()
|
||||
|
||||
def test_fileno(self) -> None:
|
||||
"""
|
||||
L{LoggingFile.fileno} returns C{-1}.
|
||||
"""
|
||||
f = LoggingFile(self.logger)
|
||||
self.assertEqual(f.fileno(), -1)
|
||||
|
||||
def test_isatty(self) -> None:
|
||||
"""
|
||||
L{LoggingFile.isatty} returns C{False}.
|
||||
"""
|
||||
f = LoggingFile(self.logger)
|
||||
self.assertFalse(f.isatty())
|
||||
|
||||
def test_writeBuffering(self) -> None:
|
||||
"""
|
||||
Writing buffers correctly.
|
||||
"""
|
||||
f = self.observedFile()
|
||||
f.write("Hello")
|
||||
self.assertEqual(f.messages, [])
|
||||
f.write(", world!\n")
|
||||
self.assertEqual(f.messages, ["Hello, world!"])
|
||||
f.write("It's nice to meet you.\n\nIndeed.")
|
||||
self.assertEqual(
|
||||
f.messages,
|
||||
[
|
||||
"Hello, world!",
|
||||
"It's nice to meet you.",
|
||||
"",
|
||||
],
|
||||
)
|
||||
|
||||
def test_writeBytesDecoded(self) -> None:
|
||||
"""
|
||||
Bytes are decoded to text.
|
||||
"""
|
||||
f = self.observedFile(encoding="utf-8")
|
||||
f.write(b"Hello, Mr. S\xc3\xa1nchez\n")
|
||||
self.assertEqual(f.messages, ["Hello, Mr. S\xe1nchez"])
|
||||
|
||||
def test_writeUnicode(self) -> None:
|
||||
"""
|
||||
Unicode is unmodified.
|
||||
"""
|
||||
f = self.observedFile(encoding="utf-8")
|
||||
f.write("Hello, Mr. S\xe1nchez\n")
|
||||
self.assertEqual(f.messages, ["Hello, Mr. S\xe1nchez"])
|
||||
|
||||
def test_writeLevel(self) -> None:
|
||||
"""
|
||||
Log level is emitted properly.
|
||||
"""
|
||||
f = self.observedFile()
|
||||
f.write("Hello\n")
|
||||
self.assertEqual(len(f.events), 1)
|
||||
self.assertEqual(f.events[0]["log_level"], LogLevel.info)
|
||||
|
||||
f = self.observedFile(level=LogLevel.error)
|
||||
f.write("Hello\n")
|
||||
self.assertEqual(len(f.events), 1)
|
||||
self.assertEqual(f.events[0]["log_level"], LogLevel.error)
|
||||
|
||||
def test_writeFormat(self) -> None:
|
||||
"""
|
||||
Log format is C{"{message}"}.
|
||||
"""
|
||||
f = self.observedFile()
|
||||
f.write("Hello\n")
|
||||
self.assertEqual(len(f.events), 1)
|
||||
self.assertEqual(f.events[0]["log_format"], "{log_io}")
|
||||
|
||||
def test_writelinesBuffering(self) -> None:
|
||||
"""
|
||||
C{writelines} does not add newlines.
|
||||
"""
|
||||
# Note this is different behavior than t.p.log.StdioOnnaStick.
|
||||
f = self.observedFile()
|
||||
f.writelines(("Hello", ", ", ""))
|
||||
self.assertEqual(f.messages, [])
|
||||
f.writelines(("world!\n",))
|
||||
self.assertEqual(f.messages, ["Hello, world!"])
|
||||
f.writelines(("It's nice to meet you.\n\n", "Indeed."))
|
||||
self.assertEqual(
|
||||
f.messages,
|
||||
[
|
||||
"Hello, world!",
|
||||
"It's nice to meet you.",
|
||||
"",
|
||||
],
|
||||
)
|
||||
|
||||
def test_print(self) -> None:
|
||||
"""
|
||||
L{LoggingFile} can replace L{sys.stdout}.
|
||||
"""
|
||||
f = self.observedFile()
|
||||
self.patch(sys, "stdout", f)
|
||||
|
||||
print("Hello,", end=" ")
|
||||
print("world.")
|
||||
|
||||
self.assertEqual(f.messages, ["Hello, world."])
|
||||
|
||||
def observedFile(
|
||||
self,
|
||||
level: NamedConstant = LogLevel.info,
|
||||
encoding: Optional[str] = None,
|
||||
) -> TestLoggingFile:
|
||||
"""
|
||||
Construct a L{LoggingFile} with a built-in observer.
|
||||
|
||||
@param level: C{level} argument to L{LoggingFile}
|
||||
@param encoding: C{encoding} argument to L{LoggingFile}
|
||||
|
||||
@return: a L{TestLoggingFile} with an observer that appends received
|
||||
events into the file's C{events} attribute (a L{list}) and
|
||||
event messages into the file's C{messages} attribute (a L{list}).
|
||||
"""
|
||||
# Logger takes an observer argument, for which we want to use the
|
||||
# TestLoggingFile we will create, but that takes the Logger as an
|
||||
# argument, so we'll use an array to indirectly reference the
|
||||
# TestLoggingFile.
|
||||
loggingFiles: List[TestLoggingFile] = []
|
||||
|
||||
@implementer(ILogObserver)
|
||||
def observer(event: LogEvent) -> None:
|
||||
loggingFiles[0](event)
|
||||
|
||||
log = Logger(observer=observer)
|
||||
loggingFiles.append(TestLoggingFile(logger=log, level=level, encoding=encoding))
|
||||
|
||||
return loggingFiles[0]
|
||||
Reference in New Issue
Block a user