# #region TestFileIOAsync [C:3] [TYPE Module] [SEMANTICS test, fileio, async, aiofiles, concurrency] # @BRIEF Async file I/O tests — verifies file operations don't block the event loop. # @RELATION BINDS_TO -> [AsyncFileIOWrappers] # @RELATION BINDS_TO -> [BlockingExecutorsModule] # @TEST_INVARIANT: async_file_io_non_blocking -> VERIFIED_BY: test_async_file_operations_dont_block_event_loop # @TEST_INVARIANT: async_file_io_basic -> VERIFIED_BY: test_aread_bytes_with_real_run_blocking import asyncio import shutil import tempfile from pathlib import Path from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch import pytest # #region test_async_file_operations_dont_block_event_loop [C:3] [TYPE Function] [SEMANTICS test, async, concurrency] # @BRIEF Verify that async file operations via run_blocking don't block the event loop. # @TEST_INVARIANT: async_file_io_non_blocking @pytest.mark.asyncio async def test_async_file_operations_dont_block_event_loop(): """Verify file operations delegated to run_blocking don't block the event loop. Strategy: run a file operation (acleanup_directory) concurrently with a fast timer. If the file operation blocks the event loop, the timer will be delayed. If it yields control (non-blocking), the timer completes on schedule. """ WORK_DELAY = 0.05 # Simulated file I/O duration async def fake_run_blocking(kind, fn, *args, **kwargs): """Simulate run_blocking: call fn synchronously but with async delay.""" if asyncio.iscoroutinefunction(fn): result = await fn(*args, **kwargs) else: result = fn(*args, **kwargs) # Simulate I/O wait without blocking await asyncio.sleep(WORK_DELAY) return result with ( patch("src.core.utils.executors.run_blocking", side_effect=fake_run_blocking), patch("src.core.utils.fileio.run_blocking", side_effect=fake_run_blocking), ): # Track timer tasks timer_events = [] async def fast_timer(sleep_s: float): """A fast timer that should complete on time if event loop is not blocked.""" await asyncio.sleep(sleep_s) timer_events.append(True) # Create a temporary directory for cleanup operation with tempfile.TemporaryDirectory() as tmpdir: test_file = Path(tmpdir) / "test_file.txt" test_file.write_text("hello") # ── Run file operation and timer concurrently ── from src.core.utils.fileio import acleanup_directory timer_sleep = WORK_DELAY * 0.5 # Timer should fire before file op completes start = asyncio.get_event_loop().time() file_task = asyncio.create_task(acleanup_directory(tmpdir)) timer_task = asyncio.create_task(fast_timer(timer_sleep)) await asyncio.gather(file_task, timer_task) elapsed = asyncio.get_event_loop().time() - start # Timer should have fired (it's shorter than file op) assert timer_events, "Timer did not fire — event loop may have been blocked" # Total time should be less than sequential (timer + file op) # If truly concurrent, total ≈ WORK_DELAY (the longer op) # If sequential, total ≈ timer_sleep + WORK_DELAY assert elapsed < WORK_DELAY + timer_sleep, ( f"File operation appears to have blocked the event loop: " f"{elapsed:.3f}s elapsed" ) # #endregion test_async_file_operations_dont_block_event_loop # #region test_aread_bytes_with_real_run_blocking [C:2] [TYPE Function] [SEMANTICS test, async, file, read] # @BRIEF Verify aread_bytes reads file bytes correctly via run_blocking executor. # @TEST_INVARIANT: async_file_io_basic @pytest.mark.asyncio async def test_aread_bytes_with_real_run_blocking(): """Verify aread_bytes returns file content correctly.""" from src.core.utils.fileio import aread_bytes from src.core.utils.executors import init_executors, shutdown_executors # Initialize executors (required by run_blocking) init_executors(file_workers=2) try: with tempfile.TemporaryDirectory() as tmpdir: test_file = Path(tmpdir) / "test_bytes.bin" test_data = b"\\x00\\x01\\x02\\x03hello" test_file.write_bytes(test_data) result = await aread_bytes(test_file) assert result == test_data, f"Expected {test_data!r}, got {result!r}" finally: shutdown_executors() # #endregion test_aread_bytes_with_real_run_blocking # #region test_async_file_temporary_directory [C:2] [TYPE Function] [SEMANTICS test, async, file, aiofiles] # @BRIEF Verify async file operations work when aiofiles is used for I/O. @pytest.mark.asyncio async def test_async_file_temporary_directory(): """Verify async file functions handle temporary files correctly.""" from src.core.utils.executors import init_executors, shutdown_executors init_executors(file_workers=2) try: with tempfile.TemporaryDirectory() as tmpdir: tmp_path = Path(tmpdir) test_content = b"async test content" # Write a test file synchronously to set up test_file = tmp_path / "test_async.txt" test_file.write_bytes(test_content) # Read via aread_dashboard_from_disk from src.core.utils.fileio import aread_dashboard_from_disk content = await aread_dashboard_from_disk(str(test_file)) assert content == test_content, ( f"aread_dashboard_from_disk returned wrong content" ) finally: shutdown_executors() # #endregion test_async_file_temporary_directory # #endregion TestFileIOAsync