156 lines
4.7 KiB
Python

import unittest
from _apple_support import SystemLog
from test.support import is_apple_mobile
from unittest.mock import Mock, call
if not is_apple_mobile:
raise unittest.SkipTest("iOS-specific")
# Test redirection of stdout and stderr to the Apple system log.
class TestAppleSystemLogOutput(unittest.TestCase):
maxDiff = None
def assert_writes(self, output):
self.assertEqual(
self.log_write.mock_calls,
[
call(self.log_level, line)
for line in output
]
)
self.log_write.reset_mock()
def setUp(self):
self.log_write = Mock()
self.log_level = 42
self.log = SystemLog(self.log_write, self.log_level, errors="replace")
def test_repr(self):
self.assertEqual(repr(self.log), "<SystemLog (level 42)>")
self.assertEqual(repr(self.log.buffer), "<LogStream (level 42)>")
def test_log_config(self):
self.assertIs(self.log.writable(), True)
self.assertIs(self.log.readable(), False)
self.assertEqual("UTF-8", self.log.encoding)
self.assertEqual("replace", self.log.errors)
self.assertIs(self.log.line_buffering, True)
self.assertIs(self.log.write_through, False)
def test_empty_str(self):
self.log.write("")
self.log.flush()
self.assert_writes([])
def test_simple_str(self):
self.log.write("hello world\n")
self.assert_writes([b"hello world\n"])
def test_buffered_str(self):
self.log.write("h")
self.log.write("ello")
self.log.write(" ")
self.log.write("world\n")
self.log.write("goodbye.")
self.log.flush()
self.assert_writes([b"hello world\n", b"goodbye."])
def test_manual_flush(self):
self.log.write("Hello")
self.assert_writes([])
self.log.write(" world\nHere for a while...\nGoodbye")
self.assert_writes([b"Hello world\n", b"Here for a while...\n"])
self.log.write(" world\nHello again")
self.assert_writes([b"Goodbye world\n"])
self.log.flush()
self.assert_writes([b"Hello again"])
def test_non_ascii(self):
# Spanish
self.log.write("ol\u00e9\n")
self.assert_writes([b"ol\xc3\xa9\n"])
# Chinese
self.log.write("\u4e2d\u6587\n")
self.assert_writes([b"\xe4\xb8\xad\xe6\x96\x87\n"])
# Printing Non-BMP emoji
self.log.write("\U0001f600\n")
self.assert_writes([b"\xf0\x9f\x98\x80\n"])
# Non-encodable surrogates are replaced
self.log.write("\ud800\udc00\n")
self.assert_writes([b"??\n"])
def test_modified_null(self):
# Null characters are logged using "modified UTF-8".
self.log.write("\u0000\n")
self.assert_writes([b"\xc0\x80\n"])
self.log.write("a\u0000\n")
self.assert_writes([b"a\xc0\x80\n"])
self.log.write("\u0000b\n")
self.assert_writes([b"\xc0\x80b\n"])
self.log.write("a\u0000b\n")
self.assert_writes([b"a\xc0\x80b\n"])
def test_nonstandard_str(self):
# String subclasses are accepted, but they should be converted
# to a standard str without calling any of their methods.
class CustomStr(str):
def splitlines(self, *args, **kwargs):
raise AssertionError()
def __len__(self):
raise AssertionError()
def __str__(self):
raise AssertionError()
self.log.write(CustomStr("custom\n"))
self.assert_writes([b"custom\n"])
def test_non_str(self):
# Non-string classes are not accepted.
for obj in [b"", b"hello", None, 42]:
with self.subTest(obj=obj):
with self.assertRaisesRegex(
TypeError,
fr"write\(\) argument must be str, not "
fr"{type(obj).__name__}"
):
self.log.write(obj)
def test_byteslike_in_buffer(self):
# The underlying buffer *can* accept bytes-like objects
self.log.buffer.write(bytearray(b"hello"))
self.log.flush()
self.log.buffer.write(b"")
self.log.flush()
self.log.buffer.write(b"goodbye")
self.log.flush()
self.assert_writes([b"hello", b"goodbye"])
def test_non_byteslike_in_buffer(self):
for obj in ["hello", None, 42]:
with self.subTest(obj=obj):
with self.assertRaisesRegex(
TypeError,
fr"write\(\) argument must be bytes-like, not "
fr"{type(obj).__name__}"
):
self.log.buffer.write(obj)