"""File I/O module tests."""

import fcntl
from unittest import mock

import pytest

from wmflib.fileio import LockError, locked_open


def try_lock_file(test_file):
    """Try to get an exclusive lock in the given file. Return True on success, False on failure."""
    with open(test_file, encoding="utf-8") as fd:
        try:
            fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
            fcntl.flock(fd, fcntl.LOCK_UN)
        except OSError:
            return False

        return True


@mock.patch("wmflib.decorators.time.sleep", return_value=None)
def test_locked_open_success(mocked_sleep, tmp_path):
    """It should acquire an exclusive lock to a file and write to it."""
    test_file = tmp_path / "acquire_lock"
    with locked_open(test_file, "w") as fd:
        fd.write("some text")
        assert not try_lock_file(test_file)

    assert try_lock_file(test_file)
    assert test_file.read_text() == "some text"
    mocked_sleep.assert_not_called()


@mock.patch("wmflib.decorators.time.sleep", return_value=None)
def test_locked_open_fail(mocked_sleep, locked_file):
    """It should retry to get an exclusive lock and raise a LockError exception on failure."""
    with pytest.raises(LockError, match="Unable to acquire exclusive lock on"), locked_open(locked_file):
        pytest.fail("Execution should not reach this point")

    mocked_sleep.assert_has_calls([mock.call(1.0)] * 9)  # 10 tries, 9 sleeps
