import errno
import multiprocessing
import os

import pytest


def test_basic(steve):
    steve.start(["-j", "5"])
    with steve.dev_path.open("r+b") as f:
        tokens = f.read(5)
        assert len(tokens) == 5
        f.write(tokens)


def non_blocking_read_test(path: Path, return_tokens: bool = True) -> None:
    with path.open("r+b") as f:
        os.set_blocking(f.fileno(), False)
        tokens = f.read(6)
        assert len(tokens) == 5
        assert f.read(1) is None
        if return_tokens:
            f.write(tokens)


def test_too_many(steve):
    steve.start(["-j", "5"])
    non_blocking_read_test(steve.dev_path)


def test_read_write_read(steve):
    steve.start(["-j", "5"])
    with steve.dev_path.open("r+b") as f:
        os.set_blocking(f.fileno(), False)
        tokens = f.read(6)
        assert len(tokens) == 5
        f.write(tokens[:2])
        tokens = tokens[2:] + f.read(6)
        assert len(tokens) == 5
        f.write(tokens)


def test_write_one_extra(steve):
    steve.start(["-j", "5"])
    with steve.dev_path.open("r+b") as f:
        # writing one is fine
        f.write(b"+")
        os.set_blocking(f.fileno(), False)
        tokens = f.read(7)
        assert len(tokens) == 6
        f.write(tokens[1:])


def test_write_too_many(steve):
    steve.start(["-j", "5"])
    with steve.dev_path.open("r+b") as f:
        tokens = f.read(5)
        assert len(tokens) == 5
        os.write(f.fileno(), tokens)
        with pytest.raises(OSError) as err:
            os.write(f.fileno(), b"12")
        assert err.value.errno == errno.ENOSPC


def test_token_reclaim(steve):
    steve.start(["-j", "5"])
    with steve.dev_path.open("r+b") as f:
        process = multiprocessing.Process(
            target=non_blocking_read_test,
            args=(steve.dev_path,),
            kwargs={"return_tokens": False},
        )
        process.start()
        process.join(10)
        assert process.exitcode == 0

        tokens = f.read(5)
        assert len(tokens) == 5
        f.write(tokens)


def test_per_process_limit(steve):
    steve.start(["-j", "12", "-p", "5"])
    with steve.dev_path.open("r+b") as f:
        os.set_blocking(f.fileno(), False)
        tokens = f.read(6)
        assert len(tokens) == 5
        assert f.read(1) is None

        process = multiprocessing.Process(
            target=non_blocking_read_test, args=(steve.dev_path,)
        )
        process.start()
        process.join(10)
        assert process.exitcode == 0

        f.write(tokens)
