# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= import asyncio import time import pytest from mirage import MountMode, Workspace, WorkspaceRunner from mirage.resource.ram import RAMResource def _make_ws() -> Workspace: return Workspace( {"/": (RAMResource(), MountMode.WRITE)}, mode=MountMode.WRITE, ) @pytest.mark.asyncio async def test_runner_executes_on_its_own_loop(): ws = _make_ws() runner = WorkspaceRunner(ws) try: outer = asyncio.get_running_loop() assert runner.loop is not outer result = await runner.call(runner.ws.execute("echo hello")) assert result.exit_code == 0 assert (result.stdout or b"").startswith(b"hello") finally: await runner.stop() @pytest.mark.asyncio async def test_runner_call_does_not_block_caller_loop(): ws = _make_ws() runner = WorkspaceRunner(ws) try: slow = asyncio.create_task(runner.call(runner.ws.execute("sleep 0.5"))) ticks = 0 for _ in range(20): await asyncio.sleep(0.05) ticks += 1 if slow.done(): break assert ticks >= 5 await slow finally: await runner.stop() @pytest.mark.asyncio async def test_two_runners_are_isolated(): ws_a = _make_ws() ws_b = _make_ws() runner_a = WorkspaceRunner(ws_a) runner_b = WorkspaceRunner(ws_b) try: slow = asyncio.create_task( runner_a.call(runner_a.ws.execute("sleep 1.0"))) await asyncio.sleep(0.05) start = time.monotonic() fast_result = await runner_b.call(runner_b.ws.execute("echo quick")) elapsed = time.monotonic() - start assert fast_result.exit_code == 0 assert elapsed < 0.5, ( f"workspace B's quick command took {elapsed:.2f}s " "while workspace A was sleeping; isolation violated") await slow finally: await runner_a.stop() await runner_b.stop() @pytest.mark.asyncio async def test_stop_is_idempotent(): ws = _make_ws() runner = WorkspaceRunner(ws) await runner.stop() await runner.stop() assert not runner._thread.is_alive() def test_call_sync_runs_on_workspace_loop(): ws = _make_ws() runner = WorkspaceRunner(ws) try: result = runner.call_sync(runner.ws.execute("echo sync"), timeout=5.0) assert result.exit_code == 0 assert (result.stdout or b"").startswith(b"sync") finally: runner.call_sync(runner.ws.close(), timeout=5.0) runner.loop.call_soon_threadsafe(runner.loop.stop) runner._thread.join(timeout=2.0) runner.loop.close()