-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_ios_log.py
More file actions
147 lines (105 loc) · 4.68 KB
/
test_ios_log.py
File metadata and controls
147 lines (105 loc) · 4.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"""Tests for pythonnative._ios_log (iOS stdout/stderr redirection).
These tests exercise the module's behavior directly without requiring
an actual iOS runtime: ``_StderrStream`` writes via ``os.write(2, ...)``,
and ``install()`` is idempotent and swaps the ``sys`` streams in place.
"""
import io
import os
import sys
from typing import Generator, List, Tuple
import pytest
from pythonnative import _ios_log
from pythonnative._ios_log import _StderrStream, install
class TestStderrStream:
def test_write_sends_utf8_bytes_to_fd_2(self, monkeypatch: pytest.MonkeyPatch) -> None:
captured: List[Tuple[int, bytes]] = []
def fake_write(fd: int, data: bytes) -> int:
captured.append((fd, data))
return len(data)
monkeypatch.setattr(os, "write", fake_write)
stream = _StderrStream()
written = stream.write("hello\n")
assert captured == [(2, b"hello\n")]
assert written == len(b"hello\n")
def test_write_handles_non_ascii(self, monkeypatch: pytest.MonkeyPatch) -> None:
captured: List[bytes] = []
def fake_write(fd: int, data: bytes) -> int:
captured.append(data)
return len(data)
monkeypatch.setattr(os, "write", fake_write)
_StderrStream().write("héllo ✓\n")
assert captured == ["héllo ✓\n".encode("utf-8")]
def test_write_empty_string_is_no_op(self, monkeypatch: pytest.MonkeyPatch) -> None:
calls: List[Tuple[int, bytes]] = []
def fake_write(fd: int, data: bytes) -> int:
calls.append((fd, data))
return len(data)
monkeypatch.setattr(os, "write", fake_write)
assert _StderrStream().write("") == 0
assert calls == []
def test_write_swallows_oserror(self, monkeypatch: pytest.MonkeyPatch) -> None:
def boom(fd: int, data: bytes) -> int:
raise OSError("nope")
monkeypatch.setattr(os, "write", boom)
assert _StderrStream().write("x") == 0
def test_writelines_iterates(self, monkeypatch: pytest.MonkeyPatch) -> None:
captured: List[bytes] = []
def fake_write(fd: int, data: bytes) -> int:
captured.append(data)
return len(data)
monkeypatch.setattr(os, "write", fake_write)
_StderrStream().writelines(["a\n", "b\n", "c\n"])
assert captured == [b"a\n", b"b\n", b"c\n"]
def test_stream_metadata(self) -> None:
stream = _StderrStream()
assert stream.encoding == "utf-8"
assert stream.errors == "replace"
assert stream.fileno() == 2
assert stream.closed is False
# flush() and close() are deliberate no-ops; just exercise them.
stream.flush()
stream.close()
def test_isatty_reflects_fd_2(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(os, "isatty", lambda fd: fd == 2)
assert _StderrStream().isatty() is True
def raiser(fd: int) -> bool:
raise OSError("bad fd")
monkeypatch.setattr(os, "isatty", raiser)
assert _StderrStream().isatty() is False
class TestInstall:
@pytest.fixture(autouse=True)
def _reset_install_flag(self) -> Generator[None, None, None]:
"""Ensure each test starts with a fresh, un-installed state."""
original_stdout = sys.stdout
original_stderr = sys.stderr
original_installed = _ios_log._installed
_ios_log._installed = False
try:
yield
finally:
sys.stdout = original_stdout
sys.stderr = original_stderr
_ios_log._installed = original_installed
def test_install_replaces_streams(self) -> None:
assert not isinstance(sys.stdout, _StderrStream)
assert not isinstance(sys.stderr, _StderrStream)
install()
assert isinstance(sys.stdout, _StderrStream)
assert isinstance(sys.stderr, _StderrStream)
def test_install_is_idempotent(self) -> None:
install()
first_stdout = sys.stdout
first_stderr = sys.stderr
install()
install()
# Second+ install() calls must be no-ops — the same objects remain.
assert sys.stdout is first_stdout
assert sys.stderr is first_stderr
def test_install_does_not_raise_when_streams_missing(self, monkeypatch: pytest.MonkeyPatch) -> None:
# Replace stdout/stderr with something odd before install to confirm
# install() doesn't care about the prior stream.
monkeypatch.setattr(sys, "stdout", io.StringIO())
monkeypatch.setattr(sys, "stderr", io.StringIO())
install()
assert isinstance(sys.stdout, _StderrStream)
assert isinstance(sys.stderr, _StderrStream)