| import unittest |
| from _apple_support import SystemLog |
| from test.support import is_apple |
| from unittest.mock import Mock, call |
| |
| if not is_apple: |
| raise unittest.SkipTest("Apple-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) |