def test_normal_commands_wait_for_and_return_response(): """ Most commands, like the info command should wait for a response. """ mock_lean_script = [ # initial sync LeanShouldGetRequest(SyncRequest(file_name="test.lean"), seq_num=1), LeanSendsResponse({"message": "file invalidated", "response": "ok", "seq_num": 1}), LeanTakesTime(.01), LeanSendsResponse({"is_running": False, "response": "current_tasks", "tasks": []}), # info request 1 LeanShouldGetRequest(InfoRequest(file_name="test.lean", line=1, column=25), seq_num=2), LeanShouldNotGetRequest(), # waiting for a lean response LeanSendsResponse({ "record": { "full-id": "max", "source": { "column": 11, "file": "path/lib/lean/library/init/algebra/functions.lean", "line": 12 }, "type": "\xce\xa0 {\xce\xb1 : Type u} [_inst_1 : decidable_linear_order \xce\xb1], \xce\xb1 \xe2\x86\x92 \xce\xb1 \xe2\x86\x92 \xce\xb1" }, "response": "ok", "seq_num": 2 }), LeanTakesTime(.01), # info request 2 LeanShouldGetRequest(InfoRequest(file_name="test.lean", line=100, column=0), seq_num=3), LeanSendsResponse({"response": "ok", "seq_num": 3}), ] async def check_behavior(): async with trio.open_nursery() as nursery: server = TrioLeanServer(nursery, debug_bytes=True) await start_with_mock_lean(server, mock_lean_script) await server.full_sync('test.lean') # check response object is parsed and of the correct type # (more parsing tests can be found in unit tests for the commands.py file) response1 = await server.send(InfoRequest(file_name="test.lean", line=1, column=25)) assert isinstance(response1, InfoResponse) assert response1.record.source.column == 11 response2 = await server.send(InfoRequest(file_name="test.lean", line=100, column=0)) assert isinstance(response2, InfoResponse) assert response2.record is None nursery.cancel_scope.cancel() trio.run(check_behavior)
def test_sleep_commands_do_not_wait_for_response(): """ The sleep commands should not wait for a response. """ mock_lean_script = [ # initial sync LeanShouldGetRequest(SyncRequest(file_name="test.lean"), seq_num=1), LeanSendsResponse({"message": "file invalidated", "response": "ok", "seq_num": 1}), LeanTakesTime(.01), LeanSendsResponse({"is_running": False, "response": "current_tasks", "tasks": []}), # Sleep request LeanShouldGetRequest(SleepRequest(), seq_num=2), # An info request sent during the sleep request. # The server should wait for a response from the sleep request LeanShouldGetRequest(InfoRequest(file_name="test.lean", line=1, column=0), seq_num=3), LeanTakesTime(.01), LeanSendsResponse({"response": "ok", "seq_num": 3}), # Long sleep request LeanShouldGetRequest(LongSleepRequest(), seq_num=4), # Lean does not wait for a response and the trio server shouldn't block # An info request sent during the sleep request. # The server should wait for a response from the sleep request LeanShouldGetRequest(InfoRequest(file_name="test.lean", line=1, column=0), seq_num=5), LeanTakesTime(.01), LeanSendsResponse({"response": "ok", "seq_num": 5}), ] async def check_behavior(): async with trio.open_nursery() as nursery: server = TrioLeanServer(nursery, debug_bytes=True) await start_with_mock_lean(server, mock_lean_script) await server.full_sync('test.lean') response1 = await server.send(SleepRequest()) assert response1 is None response2 = await server.send(InfoRequest(file_name="test.lean", line=1, column=0)) assert isinstance(response2, InfoResponse) response3 = await server.send(LongSleepRequest()) assert response3 is None response4 = await server.send(InfoRequest(file_name="test.lean", line=1, column=0)) assert isinstance(response4, InfoResponse) nursery.cancel_scope.cancel() trio.run(check_behavior)
async def state(self, filename, line, col) -> str: """Tactic state""" resp = await self.send(InfoRequest(filename, line, col)) if isinstance(resp, InfoResponse) and resp.record: return resp.record.state or '' else: return ''
async def check_behavior(): async with trio.open_nursery() as nursery: server = TrioLeanServer(nursery, debug_bytes=True) await start_with_mock_lean(server, mock_lean_script) await server.full_sync('test.lean') # check response object is parsed and of the correct type # (more parsing tests can be found in unit tests for the commands.py file) response1 = await server.send(InfoRequest(file_name="test.lean", line=1, column=25)) assert isinstance(response1, InfoResponse) assert response1.record.source.column == 11 response2 = await server.send(InfoRequest(file_name="test.lean", line=100, column=0)) assert isinstance(response2, InfoResponse) assert response2.record is None nursery.cancel_scope.cancel()
async def check_behavior(): async with trio.open_nursery() as nursery: server = TrioLeanServer(nursery, debug_bytes=True) await start_with_mock_lean(server, mock_lean_script) await server.full_sync('test.lean') response1 = await server.send(SleepRequest()) assert response1 is None response2 = await server.send(InfoRequest(file_name="test.lean", line=1, column=0)) assert isinstance(response2, InfoResponse) response3 = await server.send(LongSleepRequest()) assert response3 is None response4 = await server.send(InfoRequest(file_name="test.lean", line=1, column=0)) assert isinstance(response4, InfoResponse) nursery.cancel_scope.cancel()
def test_syncing_same_file_again(): """ If the same file is synced with no changes, then Lean won't send the same sort of responses. """ mock_lean_script = [ # initial sync LeanShouldGetRequest(SyncRequest(file_name="test.lean"), seq_num=1), LeanSendsResponse({ "message": "file invalidated", "response": "ok", "seq_num": 1 }), LeanTakesTime(.01), LeanSendsResponse({ "is_running": False, "response": "current_tasks", "tasks": [] }), # sync same file again which hasn't changed. Lean WON'T send a current_tasks response LeanShouldGetRequest(SyncRequest(file_name="test.lean"), seq_num=2), LeanSendsResponse({ "message": "file unchanged", "response": "ok", "seq_num": 2 }), LeanTakesTime(.01), # The python-lean interface should not block and instead send an info request right away LeanShouldGetRequest(InfoRequest(file_name="test.lean", line=1, column=0), seq_num=3), LeanSendsResponse({ "response": "ok", "seq_num": 3 }), ] async def check_behavior(): async with trio.open_nursery() as nursery: server = TrioLeanServer(nursery) await start_with_mock_lean(server, mock_lean_script) await server.full_sync("test.lean") await server.full_sync("test.lean") # sync same file twice await server.state(filename="test.lean", line=1, col=0) nursery.cancel_scope.cancel() trio.run(check_behavior)
def test_full_sync_waits_until_lean_ready(): """ Check that full_sync waits until the Lean server is ready. In particular, it must first get a "file invalidated" response, and then an "all_tasks" response before continuing. """ mock_lean_script = [ LeanShouldGetRequest(SyncRequest(file_name="test.lean", content="--"), seq_num=1), # current_tasks response is sent BEFORE the ok response LeanSendsResponse({ "is_running": False, "response": "current_tasks", "tasks": [] }), LeanSendsResponse({ "message": "file invalidated", "response": "ok", "seq_num": 1 }), # shouldn't be receiving anything yet LeanShouldNotGetRequest(), LeanSendsResponse({ "is_running": False, "response": "current_tasks", "tasks": [] }), # now it is ok to get a new request LeanShouldGetRequest(InfoRequest(file_name="test.lean", line=1, column=0), seq_num=2), LeanSendsResponse({ "response": "ok", "seq_num": 2 }), ] async def check_waiting_behavior(): async with trio.open_nursery() as nursery: server = TrioLeanServer(nursery) await start_with_mock_lean(server, mock_lean_script) await server.full_sync('test.lean', content='--') await server.state(filename="test.lean", line=1, col=0) nursery.cancel_scope.cancel() trio.run(check_waiting_behavior)
def test_receiver_processes_only_whole_messages(): """ Lean sometimes will not send a complet message over at a time. It may even break up a unicode character like "⊢" (three bytes). This tests that the message is properly received. """ expected_state = "⊢ true" # In bytes this is b'\xe2\x8a\xa2 true' mock_lean_script = [ # initial sync LeanShouldGetRequest(SyncRequest( file_name="test.lean", content="example : true := \nbegin end"), seq_num=1), LeanSendsResponse({ "message": "file invalidated", "response": "ok", "seq_num": 1 }), LeanTakesTime(.01), LeanSendsResponse({ "is_running": False, "response": "current_tasks", "tasks": [] }), LeanShouldGetRequest(InfoRequest(file_name="test.lean", line=2, column=0), seq_num=2), # response sent in two chunks over the stream splitting the "⊢" character b'\xe2\x8a\xa2' in half. LeanSendsBytes(b'{"record":{"state":"\xe2\x8a'), LeanTakesTime(.01), LeanSendsBytes(b'\xa2 true"},"response":"ok","seq_num":2}\n'), ] async def check_behavior(): async with trio.open_nursery() as nursery: server = TrioLeanServer(nursery, debug_bytes=True) await start_with_mock_lean(server, mock_lean_script) await server.full_sync('test.lean', content='example : true := \nbegin end') state = await server.state(filename="test.lean", line=2, col=0) assert state == "⊢ true" nursery.cancel_scope.cancel() trio.run(check_behavior)
async def check_behavior(): async with trio.open_nursery() as nursery: server = TrioLeanServer(nursery, debug_bytes=True) await start_with_mock_lean(server, mock_lean_script) await server.full_sync('test.lean') # for this test to pass it has to throw an error try: await server.send(InfoRequest(file_name="wrongfile.lean", line=1, column=0)) assert False, "An error should have been thrown here" except ChildProcessError: pass nursery.cancel_scope.cancel()
def test_errors_are_handled(): """ If a Lean server returns an error (specifically the "response" field is "error", then the Trio server should also raise an error. """ mock_lean_script = [ # initial sync LeanShouldGetRequest(SyncRequest(file_name="test.lean"), seq_num=1), LeanSendsResponse({"message": "file invalidated", "response": "ok", "seq_num": 1}), LeanTakesTime(.01), LeanSendsResponse({"is_running": False, "response": "current_tasks", "tasks": []}), # An info request sent during the sleep request. # The server should wait for a response from the sleep request LeanShouldGetRequest(InfoRequest(file_name="wrongfile.lean", line=1, column=0), seq_num=2), LeanTakesTime(.01), LeanSendsResponse({ "message": "file \'wrongfile.lean\' not found in the LEAN_PATH", "response": "error", "seq_num": 2 }) ] async def check_behavior(): async with trio.open_nursery() as nursery: server = TrioLeanServer(nursery, debug_bytes=True) await start_with_mock_lean(server, mock_lean_script) await server.full_sync('test.lean') # for this test to pass it has to throw an error try: await server.send(InfoRequest(file_name="wrongfile.lean", line=1, column=0)) assert False, "An error should have been thrown here" except ChildProcessError: pass nursery.cancel_scope.cancel() trio.run(check_behavior)
def info(self, filename, line, col): """Send info query to Lean.""" self.send(InfoRequest(filename, line, col))