from __future__ import annotations

import attrs
import pytest

from .. import abc as tabc
from ..lowlevel import Task


def test_instrument_implements_hook_methods() -> None:
    attrs = {
        "before_run": (),
        "after_run": (),
        "task_spawned": (Task,),
        "task_scheduled": (Task,),
        "before_task_step": (Task,),
        "after_task_step": (Task,),
        "task_exited": (Task,),
        "before_io_wait": (3.3,),
        "after_io_wait": (3.3,),
    }

    mayonnaise = tabc.Instrument()

    for method_name, args in attrs.items():
        assert hasattr(mayonnaise, method_name)
        method = getattr(mayonnaise, method_name)
        assert callable(method)
        method(*args)


async def test_AsyncResource_defaults() -> None:
    @attrs.define(slots=False)
    class MyAR(tabc.AsyncResource):
        record: list[str] = attrs.Factory(list)

        async def aclose(self) -> None:
            self.record.append("ac")

    async with MyAR() as myar:
        assert isinstance(myar, MyAR)
        assert myar.record == []

    assert myar.record == ["ac"]


def test_abc_generics() -> None:
    # Pythons below 3.5.2 had a typing.Generic that would throw
    # errors when instantiating or subclassing a parameterized
    # version of a class with any __slots__. This is why RunVar
    # (which has slots) is not generic. This tests that
    # the generic ABCs are fine, because while they are slotted
    # they don't actually define any slots.

    class SlottedChannel(tabc.SendChannel[tabc.Stream]):
        __slots__ = ("x",)

        def send_nowait(self, value: object) -> None:
            raise RuntimeError

        async def send(self, value: object) -> None:
            raise RuntimeError  # pragma: no cover

        def clone(self) -> None:
            raise RuntimeError  # pragma: no cover

        async def aclose(self) -> None:
            pass  # pragma: no cover

    channel = SlottedChannel()
    with pytest.raises(RuntimeError):
        channel.send_nowait(None)
