-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
gh-126925: Modify how iOS test results are gathered #127592
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 7 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
9385e89
Add use_system_log config item, with redirection for Apple platforms.
freakboy3742 db31721
Add a testbed runner script with log streaming.
freakboy3742 60584e0
Add NEWS entries.
freakboy3742 68253aa
Add timestamp to track when simulator is detected.
freakboy3742 3dc0d71
Add use_system_logger support to the embed tests.
freakboy3742 0b9baa1
Modifications to make testbed runner more flexible and robust.
freakboy3742 89bb435
Add documentation for using the testbed runner.
freakboy3742 0c0d1fc
Add punctuation to datetime formatting.
freakboy3742 cbcc044
Correct a reference to Android in a docstring.
freakboy3742 0532c62
Suppress duplicate 'Messages dropped' messages.
freakboy3742 d9ef983
Add Whats New entries for streaming changes.
freakboy3742 47870c4
Merge branch 'main' into ios-testbed-wrapper
freakboy3742 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import io | ||
import sys | ||
|
||
|
||
def init_streams(log_write, stdout_level, stderr_level): | ||
# Redirect stdout and stderr to the Apple system log. This method is | ||
# invoked by init_apple_streams() (initconfig.c) if config->use_system_logger | ||
# is enabled. | ||
sys.stdout = SystemLog(log_write, stdout_level, errors=sys.stderr.errors) | ||
sys.stderr = SystemLog(log_write, stderr_level, errors=sys.stderr.errors) | ||
|
||
|
||
class SystemLog(io.TextIOWrapper): | ||
def __init__(self, log_write, level, **kwargs): | ||
kwargs.setdefault("encoding", "UTF-8") | ||
kwargs.setdefault("line_buffering", True) | ||
super().__init__(LogStream(log_write, level), **kwargs) | ||
|
||
def __repr__(self): | ||
return f"<SystemLog (level {self.buffer.level})>" | ||
|
||
def write(self, s): | ||
if not isinstance(s, str): | ||
raise TypeError( | ||
f"write() argument must be str, not {type(s).__name__}") | ||
|
||
# In case `s` is a str subclass that writes itself to stdout or stderr | ||
# when we call its methods, convert it to an actual str. | ||
s = str.__str__(s) | ||
|
||
# We want to emit one log message per line, so split | ||
# the string before sending it to the superclass. | ||
for line in s.splitlines(keepends=True): | ||
super().write(line) | ||
|
||
return len(s) | ||
|
||
|
||
class LogStream(io.RawIOBase): | ||
def __init__(self, log_write, level): | ||
self.log_write = log_write | ||
self.level = level | ||
|
||
def __repr__(self): | ||
return f"<LogStream (level {self.level!r})>" | ||
|
||
def writable(self): | ||
return True | ||
|
||
def write(self, b): | ||
if type(b) is not bytes: | ||
try: | ||
b = bytes(memoryview(b)) | ||
except TypeError: | ||
raise TypeError( | ||
f"write() argument must be bytes-like, not {type(b).__name__}" | ||
) from None | ||
|
||
# Writing an empty string to the stream should have no effect. | ||
if b: | ||
# Encode null bytes using "modified UTF-8" to avoid truncating the | ||
# message. This should not affect the return value, as the caller | ||
# may be expecting it to match the length of the input. | ||
self.log_write(self.level, b.replace(b"\x00", b"\xc0\x80")) | ||
|
||
return len(b) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
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 Android 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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
Misc/NEWS.d/next/Library/2024-12-04-15-04-12.gh-issue-126821.lKCLVV.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
macOS and iOS apps can now choose to redirect stdout and stderr to the | ||
system log during interpreter configuration. |
2 changes: 2 additions & 0 deletions
2
Misc/NEWS.d/next/Tests/2024-12-04-15-03-24.gh-issue-126925.uxAMK-.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
iOS test results are now streamed during test execution, and the deprecated | ||
xcresulttool is no longer used. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/Android log/?/