Skip to content
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

get and enforce 100% coverage #3159

Merged
merged 42 commits into from
Dec 21, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
2aa5771
test waitid on linux even with pidfd support
graingert Dec 19, 2024
8501b0c
refactor duplicate code in gen_exports
graingert Dec 19, 2024
be08460
more coverage in test_dtls
graingert Dec 19, 2024
2722418
cover monitors
graingert Dec 19, 2024
8858398
test cancel wait without pidfd
graingert Dec 19, 2024
e5fa8cd
test OSError in waitid
graingert Dec 19, 2024
340373d
Update src/trio/_core/_tests/test_io.py
graingert Dec 19, 2024
d436b06
Update src/trio/_core/_tests/test_io.py
graingert Dec 20, 2024
0ac717d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 20, 2024
1398349
Merge branch 'main' into 100-coverage
graingert Dec 20, 2024
34d6399
filterwarning
graingert Dec 20, 2024
818094a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 20, 2024
3dac42d
try two assertions
graingert Dec 20, 2024
dda9a27
ok what, this is very strange, but works.
graingert Dec 20, 2024
bd81624
no cover some unreachable pytest.fail code
graingert Dec 20, 2024
640f581
remove some unused code from test_subprocess.py
graingert Dec 20, 2024
50d848d
always use SSL_OP_NO_TLSv1_3 - now that it's available
graingert Dec 20, 2024
28ee44a
cowardly hide from the coverage check using a ternery
graingert Dec 20, 2024
ac30b6b
Revert "more coverage in test_dtls"
graingert Dec 20, 2024
f312a70
an empty string joined with '' is a noop, so this branch is redundant
graingert Dec 20, 2024
badc910
test errors in CancelScope ctor
graingert Dec 20, 2024
a57247a
cover code in the contextvars counter
graingert Dec 20, 2024
5344e71
test more of kqueue
graingert Dec 20, 2024
8f05dbf
actually enter the cmgr
graingert Dec 20, 2024
1ec824c
no cover unreachable cmgr inside
graingert Dec 20, 2024
a0f33ee
remove await - it seems to timeout
graingert Dec 20, 2024
cc4b217
add newsfragment
graingert Dec 20, 2024
24917b7
bump the lower end of the coverage range
graingert Dec 20, 2024
68de044
add todo to nocov
graingert Dec 20, 2024
95b1f75
ignore some coverage with TODOs
graingert Dec 20, 2024
becdf2a
assert there's no node in cached_type_info
graingert Dec 20, 2024
3f34731
require 100% coverage
graingert Dec 20, 2024
bd097dd
Update .codecov.yml
graingert Dec 20, 2024
dcfe01a
Merge branch 'main' into 100-coverage
graingert Dec 20, 2024
431f2ed
more TODO comments
graingert Dec 20, 2024
117fe04
add one last TODO
graingert Dec 20, 2024
84de11a
fix patches comment in .codecov.yml
graingert Dec 20, 2024
2484749
swap branch for line in dtls
graingert Dec 20, 2024
d055b38
Update newsfragments/3159.misc.rst
graingert Dec 20, 2024
afa13f5
Update src/trio/_tests/test_exports.py
graingert Dec 21, 2024
e78f317
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 21, 2024
d962e05
Update src/trio/_tools/gen_exports.py
graingert Dec 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/trio/_core/_tests/test_io.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from __future__ import annotations

import random
import select
import socket as stdlib_socket
import sys
from collections.abc import Awaitable, Callable
from contextlib import suppress
from typing import TYPE_CHECKING, TypeVar
Expand Down Expand Up @@ -343,6 +345,7 @@
assert iostats.tasks_waiting_write == expected_writers
else:
assert iostats.backend == "kqueue"
assert iostats.monitors == 0
assert iostats.tasks_waiting == expected_readers + expected_writers

a1, b1 = stdlib_socket.socketpair()
Expand Down Expand Up @@ -381,6 +384,38 @@
check(expected_readers=1, expected_writers=0)


@pytest.mark.skipif(sys.platform in {"win32", "linux"}, reason="no kqueue")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From mypy run annotations, looks like you need to inform mypy that you don't expect this function to run on windows or linux, because mypy doesn't understand pytest.mark.skipif.
I suggest something like this:

if sys.platform == "win32" or sys.platform == "linux":
    raise AssertionError("Should be unreachable")

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CoolCat467 perhaps, assert_never()?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or better yet assert sys.platform not in {"win32", "linux"}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't get either option working (assert sys.platform != "win32" and sys.platform != "linux") does work in the playground, and if I create a new file.

async def test_io_manager_kqueue_monitors_statistics() -> None:
graingert marked this conversation as resolved.
Show resolved Hide resolved
def check(
*,
expected_monitors: int,
expected_readers: int,
expected_writers: int,
) -> None:
statistics = _core.current_statistics()
print(statistics)
iostats = statistics.io_statistics
assert iostats.backend == "kqueue"
assert iostats.monitors == expected_monitors
assert iostats.tasks_waiting == expected_readers + expected_writers

a1, b1 = stdlib_socket.socketpair()
for sock in [a1, b1]:
sock.setblocking(False)

with a1, b1:
# let the call_soon_task settle down
await wait_all_tasks_blocked()

# 1 for call_soon_task
check(expected_monitors=0, expected_readers=1, expected_writers=0)

with trio.lowlevel.monitor_kevent(a1.fileno(), select.KQ_FILTER_READ):

Check failure on line 413 in src/trio/_core/_tests/test_io.py

View workflow job for this annotation

GitHub Actions / Ubuntu (3.13, check formatting)

Mypy-Linux+Windows

src/trio/_core/_tests/test_io.py:(413:14 - 413:41): Module has no attribute "monitor_kevent" [attr-defined]

Check failure on line 413 in src/trio/_core/_tests/test_io.py

View workflow job for this annotation

GitHub Actions / Ubuntu (3.13, check formatting)

Mypy-Linux+Windows

src/trio/_core/_tests/test_io.py:(413:56 - 413:76): Module has no attribute "KQ_FILTER_READ" [attr-defined]
check(expected_monitors=1, expected_readers=1, expected_writers=0)

check(expected_monitors=0, expected_readers=1, expected_writers=0)


async def test_can_survive_unnotified_close() -> None:
# An "unnotified" close is when the user closes an fd/socket/handle
# directly, without calling notify_closing first. This should never happen
Expand Down
12 changes: 12 additions & 0 deletions src/trio/_tests/test_dtls.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ async def test_smoke(ipv6: bool) -> None:
assert client_channel.get_cleartext_mtu() == cleartext_mtu_1234


@parametrize_ipv6
async def test_smoke_close_immediately(ipv6: bool) -> None:
# used to get 100% branch coverage in this file itself
async with dtls_echo_server(ipv6=ipv6) as (_server_endpoint, address):
with endpoint(ipv6=ipv6) as client_endpoint:
client_channel = client_endpoint.connect(address, client_ctx)
with pytest.raises(trio.NeedHandshakeError):
client_channel.get_cleartext_mtu()

await client_channel.do_handshake()


@slow
async def test_handshake_over_terrible_network(
autojump_clock: trio.testing.MockClock,
Expand Down
67 changes: 67 additions & 0 deletions src/trio/_tests/test_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
Any,
NoReturn,
)
from unittest import mock

import pytest

Expand Down Expand Up @@ -146,6 +147,26 @@ async def test_basic(background_process: BackgroundProcessType) -> None:
)


@background_process_param
async def test_basic_no_pidfd(background_process: BackgroundProcessType) -> None:
with mock.patch("trio._subprocess.can_try_pidfd_open", new=False):
async with background_process(EXIT_TRUE) as proc:
assert proc._pidfd is None
await proc.wait()
assert isinstance(proc, Process)
assert proc._pidfd is None
assert proc.returncode == 0
assert repr(proc) == f"<trio.Process {EXIT_TRUE}: exited with status 0>"

async with background_process(EXIT_FALSE) as proc:
await proc.wait()
assert proc.returncode == 1
assert repr(proc) == "<trio.Process {!r}: {}>".format(
EXIT_FALSE,
"exited with status 1",
)


@background_process_param
async def test_auto_update_returncode(
background_process: BackgroundProcessType,
Expand Down Expand Up @@ -181,6 +202,27 @@ async def test_multi_wait(background_process: BackgroundProcessType) -> None:
proc.kill()


@background_process_param
async def test_multi_wait_no_pidfd(background_process: BackgroundProcessType) -> None:
with mock.patch("trio._subprocess.can_try_pidfd_open", new=False):
async with background_process(SLEEP(10)) as proc:
# Check that wait (including multi-wait) tolerates being cancelled
async with _core.open_nursery() as nursery:
nursery.start_soon(proc.wait)
nursery.start_soon(proc.wait)
nursery.start_soon(proc.wait)
await wait_all_tasks_blocked()
nursery.cancel_scope.cancel()

# Now try waiting for real
async with _core.open_nursery() as nursery:
nursery.start_soon(proc.wait)
nursery.start_soon(proc.wait)
nursery.start_soon(proc.wait)
await wait_all_tasks_blocked()
proc.kill()


COPY_STDIN_TO_STDOUT_AND_BACKWARD_TO_STDERR = python(
"data = sys.stdin.buffer.read(); "
"sys.stdout.buffer.write(data); "
Expand Down Expand Up @@ -524,6 +566,31 @@ async def test_wait_reapable_fails(background_process: BackgroundProcessType) ->
signal.signal(signal.SIGCHLD, old_sigchld)


@pytest.mark.skipif(not posix, reason="POSIX specific")
@background_process_param
async def test_wait_reapable_fails_no_pidfd(
background_process: BackgroundProcessType,
) -> None:
if TYPE_CHECKING and sys.platform == "win32":
return
with mock.patch("trio._subprocess.can_try_pidfd_open", new=False):
old_sigchld = signal.signal(signal.SIGCHLD, signal.SIG_IGN)
try:
# With SIGCHLD disabled, the wait() syscall will wait for the
# process to exit but then fail with ECHILD. Make sure we
# support this case as the stdlib subprocess module does.
async with background_process(SLEEP(3600)) as proc:
async with _core.open_nursery() as nursery:
nursery.start_soon(proc.wait)
await wait_all_tasks_blocked()
proc.kill()
nursery.cancel_scope.deadline = _core.current_time() + 1.0
assert not nursery.cancel_scope.cancelled_caught
assert proc.returncode == 0 # exit status unknowable, so...
finally:
signal.signal(signal.SIGCHLD, old_sigchld)


@slow
def test_waitid_eintr() -> None:
# This only matters on PyPy (where we're coding EINTR handling
Expand Down
23 changes: 7 additions & 16 deletions src/trio/_tools/gen_exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,22 +180,13 @@ def run_linters(file: File, source: str) -> str:
SystemExit: If either failed.
"""

success, response = run_black(file, source)
if not success:
print(response)
sys.exit(1)

success, response = run_ruff(file, response)
if not success: # pragma: no cover # Test for run_ruff should catch
print(response)
sys.exit(1)

success, response = run_black(file, response)
if not success:
print(response)
sys.exit(1)

return response
for fn in (run_black, run_ruff, run_black):
graingert marked this conversation as resolved.
Show resolved Hide resolved
success, source = fn(file, source)
if not success:
print(source)
sys.exit(1)

return source


def gen_public_wrappers_source(file: File) -> str:
Expand Down
Loading