def test_debugger_core_hit_condition_breakpoint(debugger_api, robot_thread) -> None: from robotframework_debug_adapter.debugger_impl import install_robot_debugger from robotframework_debug_adapter.debugger_impl import RobotBreakpoint debugger_impl = install_robot_debugger() target = debugger_api.get_dap_case_file("case_condition.robot") line = debugger_api.get_line_index_with_content("Log ${counter}", target) debugger_impl.set_breakpoints(target, RobotBreakpoint(line, hit_condition=2)) robot_thread.run_target(target) # It should only stop once (when counter == 2). dbg_wait_for(lambda: debugger_impl.busy_wait.waited == 1) thread_id = debugger_impl.get_current_thread_id(robot_thread) frame_ids = list(debugger_impl.iter_frame_ids(thread_id)) eval_info = debugger_impl.evaluate(frame_ids[0], "${counter}") assert eval_info.future.result() == 2 debugger_impl.step_continue() dbg_wait_for(lambda: robot_thread.result_code == 0)
def check_waited(expected): def msg(): return "Expected waited to be: %s. Found: %s" % ( expected, debugger_impl.busy_wait.waited, ) dbg_wait_for(lambda: debugger_impl.busy_wait.waited == expected, msg=msg)
def test_debugger_core_evaluate(debugger_api_core, robot_thread, tmpdir) -> None: from robotframework_debug_adapter.debugger_impl import install_robot_debugger from robotframework_debug_adapter.debugger_impl import RobotBreakpoint from robotframework_debug_adapter.constants import MAIN_THREAD_ID from robotframework_debug_adapter.debugger_impl import InvalidFrameIdError from robotframework_debug_adapter.debugger_impl import InvalidFrameTypeError debugger_impl = install_robot_debugger() target = debugger_api_core.get_dap_case_file("case_evaluate.robot") debugger_api_core.target = target line = debugger_api_core.get_line_index_with_content("Break 1") debugger_impl.set_breakpoints(target, RobotBreakpoint(line)) robot_thread.run_target(target) dbg_wait_for(lambda: debugger_impl.busy_wait.waited == 1) try: invalid_frame_id = -11 filename = str(tmpdir.join("file.txt")) filename = filename.replace("\\", "/") content = "file.txt" frame_ids = list(debugger_impl.iter_frame_ids(MAIN_THREAD_ID)) # Fail due to invalid frame id eval_info = debugger_impl.evaluate( invalid_frame_id, "Create File %s content=%s" % (filename, content)) with pytest.raises(InvalidFrameIdError): eval_info.future.result() # Fail because the stack selected is not a keyword stack. eval_info = debugger_impl.evaluate( frame_ids[-1], "Create File %s content=%s" % (filename, content)) with pytest.raises(InvalidFrameTypeError): eval_info.future.result() # Keyword evaluation works eval_info = debugger_impl.evaluate( frame_ids[0], "Create File %s content=%s" % (filename, content)) assert eval_info.future.result() is None with open(filename, "r") as stream: contents = stream.read() assert contents == content # Get variable in evaluation works eval_info = debugger_impl.evaluate(frame_ids[0], "${arg1}") assert eval_info.future.result() == "2" eval_info = debugger_impl.evaluate(frame_ids[0], "${ARG1}") assert eval_info.future.result() == "2" finally: debugger_impl.step_continue()
def test_debugger_core_if(debugger_api, robot_thread, data_regression) -> None: from robotframework_debug_adapter.debugger_impl import install_robot_debugger from robotframework_debug_adapter.debugger_impl import RobotBreakpoint from robotframework_debug_adapter.constants import MAIN_THREAD_ID debugger_impl = install_robot_debugger() target = debugger_api.get_dap_case_file( "case_control_flow/case_control_flow_if.robot") line = debugger_api.get_line_index_with_content("Break 1", target) debugger_impl.set_breakpoints(target, RobotBreakpoint(line)) robot_thread.run_target(target) stack_lst: List[Optional[List[StackFrame]]] = [] try: dbg_wait_for(lambda: debugger_impl.busy_wait.waited == 1) stack_lst.append(debugger_impl.get_frames(MAIN_THREAD_ID)) debugger_impl.step_in() dbg_wait_for(lambda: debugger_impl.busy_wait.waited == 2) stack_lst.append(debugger_impl.get_frames(MAIN_THREAD_ID)) finally: debugger_impl.step_in() # Will actually finish the program now. dbg_wait_for(lambda: debugger_impl.busy_wait.proceeded == 2) data_regression.check(stack_frames_repr(stack_lst)) dbg_wait_for(lambda: robot_thread.result_code == 0)
def test_debugger_core_keyword_if(debugger_api, robot_thread, data_regression) -> None: from robotframework_debug_adapter.debugger_impl import install_robot_debugger from robotframework_debug_adapter.debugger_impl import RobotBreakpoint from robotframework_debug_adapter.constants import MAIN_THREAD_ID debugger_impl = install_robot_debugger() target = debugger_api.get_dap_case_file( "case_control_flow/case_control_flow_for.robot") line = debugger_api.get_line_index_with_content("Break 2", target) debugger_impl.set_breakpoints(target, RobotBreakpoint(line)) robot_thread.run_target(target) stack_lst = [] def check_waited(expected): def msg(): return "Expected waited to be: %s. Found: %s" % ( expected, debugger_impl.busy_wait.waited, ) dbg_wait_for(lambda: debugger_impl.busy_wait.waited == expected, msg=msg) try: check_waited(1) stack_lst.append(debugger_impl.get_frames(MAIN_THREAD_ID)) debugger_impl.step_in() check_waited(2) stack_lst.append(debugger_impl.get_frames(MAIN_THREAD_ID)) debugger_impl.step_in() check_waited(3) stack_lst.append(debugger_impl.get_frames(MAIN_THREAD_ID)) debugger_impl.step_in() check_waited(4) stack_lst.append(debugger_impl.get_frames(MAIN_THREAD_ID)) finally: debugger_impl.step_continue() dbg_wait_for(lambda: debugger_impl.busy_wait.proceeded == 4) data_regression.check(stack_frames_repr(stack_lst)) dbg_wait_for(lambda: robot_thread.result_code == 0)
def test_debugger_core(debugger_api_core, robot_thread) -> None: from robotframework_debug_adapter.debugger_impl import install_robot_debugger from robotframework_debug_adapter.debugger_impl import RobotBreakpoint debugger_impl = install_robot_debugger() target = debugger_api_core.get_dap_case_file("case_log.robot") debugger_api_core.target = target line = debugger_api_core.get_line_index_with_content( "check that log works") debugger_impl.set_breakpoints(target, RobotBreakpoint(line)) robot_thread.run_target(target) thread_id = debugger_impl.get_current_thread_id(robot_thread) try: dbg_wait_for(lambda: debugger_impl.busy_wait.waited == 1) stack = debugger_impl.get_frames(thread_id) finally: debugger_impl.step_continue() dbg_wait_for(lambda: debugger_impl.busy_wait.proceeded == 1) assert stack and len(stack) == 3 dbg_wait_for(lambda: robot_thread.result_code == 0)
def test_debug_adapter_threaded( debugger_api_core, dap_log_file, robot_thread, dap_logs_dir ): """ This is an example on how to setup the debugger structure in-memory but still talking through the DAP instead of using the core APIs. debugger_api_core: helper to get file to run / compute breakpoint position. dap_log_file: a place to store logs. robot_thread: helper run robot in a thread. dap_logs_dir: another api to store the logs needed. """ import robotframework_ls from robotframework_debug_adapter_tests.fixtures import dbg_wait_for from robocorp_ls_core.debug_adapter_core.debug_adapter_threads import ( STOP_WRITER_THREAD, ) robotframework_ls.import_robocorp_ls_core() # Configure the logger from robocorp_ls_core.robotframework_log import configure_logger reader, writer = None, None with configure_logger("robot", 3, dap_log_file): # i.e: create the server socket which will wait for the debugger connection. client_thread = _ClientThread() try: client_thread.start() address = client_thread.get_address() from robotframework_debug_adapter.run_robot__main__ import ( connect, _RobotTargetComm, ) # Note: _RobotTargetComm takes care of initializing the debugger. processor = _RobotTargetComm(connect(address[1]), debug=True) reader, writer = processor.start_communication_threads( mark_as_pydevd_threads=False ) assert client_thread.started.wait(2) # Ok, at this point the setup should be done, let's set a breakpoint # then ask to run and see if it stops there. target = debugger_api_core.get_dap_case_file("case_log.robot") debugger_api_core.target = target line = debugger_api_core.get_line_index_with_content("check that log works") client_thread.debugger_api.set_breakpoints(target, line) # Actually run it (do it in a thread so that we can verify # things on this thread). robot_thread.run_target(target) json_hit = client_thread.debugger_api.wait_for_thread_stopped( file=target, line=line ) client_thread.debugger_api.continue_event(json_hit.thread_id) dbg_wait_for( lambda: robot_thread.result_code is not None, msg="Robot execution did not finish properly.", ) # Run it once more to check that the communication is still in place # -- i.e.: we don't actually terminate it if the connection was done # in memory. from robotframework_debug_adapter_tests.fixtures import RunRobotThread robot_thread2 = RunRobotThread(dap_logs_dir) robot_thread2.run_target(target) json_hit = client_thread.debugger_api.wait_for_thread_stopped( file=target, line=line ) client_thread.debugger_api.continue_event(json_hit.thread_id) dbg_wait_for( lambda: robot_thread2.result_code is not None, msg="Robot execution did not finish properly.", ) finally: # Upon finish the client should close the sockets to finish the readers. client_thread.finish.set() # Upon finish ask the writer thread to exit too (it won't automatically finish # even if sockets are closed). processor.write_message(STOP_WRITER_THREAD) assert client_thread.sockets_closed.wait(1) if reader is not None: reader.join(2) assert not reader.is_alive(), "Expected reader to have exited already." if writer is not None: writer.join(2) assert not writer.is_alive(), "Expected writer to have exited already."
def test_debugger_core_for(debugger_api, robot_thread, data_regression) -> None: from robotframework_debug_adapter.debugger_impl import install_robot_debugger from robotframework_debug_adapter.debugger_impl import RobotBreakpoint from robotframework_debug_adapter.constants import MAIN_THREAD_ID debugger_impl = install_robot_debugger() target = debugger_api.get_dap_case_file( "case_control_flow/case_control_flow_for.robot") line = debugger_api.get_line_index_with_content("Break 1", target) debugger_impl.set_breakpoints(target, RobotBreakpoint(line)) robot_thread.run_target(target) stack_lst: List[Optional[List[StackFrame]]] = [] try: dbg_wait_for(lambda: debugger_impl.busy_wait.waited == 1) stack_lst.append(debugger_impl.get_frames(MAIN_THREAD_ID)) debugger_impl.step_in() dbg_wait_for(lambda: debugger_impl.busy_wait.waited == 2) stack_lst.append(debugger_impl.get_frames(MAIN_THREAD_ID)) debugger_impl.step_in() dbg_wait_for(lambda: debugger_impl.busy_wait.waited == 3) stack_lst.append(debugger_impl.get_frames(MAIN_THREAD_ID)) debugger_impl.step_in() dbg_wait_for(lambda: debugger_impl.busy_wait.waited == 4) stack_lst.append(debugger_impl.get_frames(MAIN_THREAD_ID)) n_proceeded = 4 if IS_ROBOT_4_ONWARDS: # We have additional steps as we get one step with the creation # of the ${counter} variable when stepping into the for. debugger_impl.step_in() dbg_wait_for(lambda: debugger_impl.busy_wait.waited == 5) stack_lst.append(debugger_impl.get_frames(MAIN_THREAD_ID)) debugger_impl.step_in() dbg_wait_for(lambda: debugger_impl.busy_wait.waited == 6) stack_lst.append(debugger_impl.get_frames(MAIN_THREAD_ID)) n_proceeded = 6 finally: debugger_impl.step_continue() dbg_wait_for(lambda: debugger_impl.busy_wait.proceeded == n_proceeded) if IS_ROBOT_4_ONWARDS: basename = "test_debugger_core_for.v4" else: basename = "test_debugger_core_for.v3" data_regression.check(stack_frames_repr(stack_lst), basename=basename) dbg_wait_for(lambda: robot_thread.result_code == 0)