async def test_RLock_valid_setup():
    lock_1 = asyncio_tools.RLock()
    lock_2 = asyncio_tools.RLock()

    passed_a = passed_b = passed_c = passed_d = passed_e = False
    async with lock_1:
        passed_a = True
        async with lock_1:
            passed_b = True
            assert lock_1._depth == 2
            async with lock_2:
                passed_c = True
                async with lock_2:
                    assert lock_2._depth == 2
                    passed_d = True
                    async with lock_1:
                        assert lock_1._task is asyncio.current_task()
                        assert lock_2._task is asyncio.current_task()
                        assert lock_1._depth == 3
                        passed_e = True
                    assert lock_1._depth == 2
    assert lock_1._depth == 0
    assert lock_2._depth == 0
    assert lock_1._locked is False
    assert lock_2._locked is False
    assert lock_1._task is None
    assert lock_2._task is None
    assert all((passed_a, passed_b, passed_c, passed_d, passed_e))
async def test_RLock_error_setup_2():
    lock = asyncio_tools.RLock()
    lock._depth = 1

    # a lock can't have a non 0 depth when firstly acquired
    with pytest.raises(RuntimeError):
        async with lock:
            pass
    def __init__(self, exchange_name, is_simulated=False):
        super().__init__()
        self._exchange_name = exchange_name
        self._is_simulated = is_simulated

        self.logger = logging.get_logger(
            f"{self.__class__.__name__}{'Simulator' if is_simulated else ''}[{exchange_name}]"
        )
        self.lock = asyncio_tools.RLock()
        self.portfolio = None
        self.reset()
async def test_RLock_error_setup_1():
    lock = asyncio_tools.RLock()

    passed_a = passed_b = False

    # _depth is too high: lock is not cleared
    async with lock:
        passed_a = True
        lock._depth += 1
        async with lock:
            passed_b = True
            assert lock._depth == 3
    assert lock._depth == 1
    assert lock._task is asyncio.current_task()
    assert lock._locked is True

    assert all((passed_a, passed_b))
async def test_RLock_multiple_tasks():
    lock = asyncio_tools.RLock()
    started = {
        "a": False,
        "b": False,
        "c": False,
    }
    passed = {
        "a": False,
        "b": False,
        "c": False,
    }
    released = {
        "a": False,
        "b": False,
        "c": False,
    }

    async def passing_task(identifier, is_waiting):
        started[identifier] = True
        async with lock:
            if is_waiting:
                await asyncio_tools.wait_asyncio_next_cycle()
            assert lock._depth == 1
            assert lock._task == asyncio.current_task()
            passed[identifier] = True
        released[identifier] = True

    asyncio.create_task(passing_task("a", True))
    asyncio.create_task(passing_task("b", False))
    asyncio.create_task(passing_task("c", False))

    # tasks did not run yet
    assert all(v is False for v in started.values())

    # let the loop run once
    await asyncio_tools.wait_asyncio_next_cycle()

    # all tasks started
    assert started == {
        "a": True,
        "b": True,
        "c": True,
    }
    # "a" waits for next cycle, others wait for lock that "a" got
    assert passed == {
        "a": False,
        "b": False,
        "c": False,
    }
    assert released == {
        "a": False,
        "b": False,
        "c": False,
    }
    await asyncio_tools.wait_asyncio_next_cycle()
    # "a" release the lock, "b" can get it
    assert passed == {
        "a": True,
        "b": True,
        "c": False,
    }
    assert released == {
        "a": True,
        "b": True,
        "c": False,
    }
    # "b" release the lock, "c" can get it
    await asyncio_tools.wait_asyncio_next_cycle()

    assert all(v is True for v in passed.values())
    assert all(v is True for v in released.values())