From e9a8f2a76e061ee4793d3a57563f5de8aef52673 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 17:32:01 +0000 Subject: [PATCH 1/2] Initial plan From 0de0b7ca529acdb808e55584daca1f33529ba306 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 17:37:55 +0000 Subject: [PATCH 2/2] Consolidate duplicate and nested imports in test files Agent-Logs-Url: https://github.com/NetApp/recline/sessions/7a2eaa6c-36b6-4202-a8a1-aae1977a52f9 Co-authored-by: RobertBlackhart <5414318+RobertBlackhart@users.noreply.github.com> --- tests/test_commands/test_async_command.py | 39 ++++---------- tests/test_commands/test_builtin_commands.py | 23 +-------- tests/test_repl/test_shell.py | 54 ++++---------------- 3 files changed, 23 insertions(+), 93 deletions(-) diff --git a/tests/test_commands/test_async_command.py b/tests/test_commands/test_async_command.py index d8c047a..f38fa7a 100644 --- a/tests/test_commands/test_async_command.py +++ b/tests/test_commands/test_async_command.py @@ -5,11 +5,21 @@ """ import asyncio +import os +import sys +import termios +import threading +import time import pytest import recline -from recline.commands.async_command import AsyncCommand +from recline.commands.async_command import ( + AsyncCommand, + CommandBackgrounded, + CommandCancelled, + set_terminal_echo, +) @pytest.mark.usefixtures("clean_jobs") @@ -111,8 +121,6 @@ async def test_coro(): def test_command_backgrounded_exception(): """Verify CommandBackgrounded carries its job_pid attribute.""" - from recline.commands.async_command import CommandBackgrounded - exc = CommandBackgrounded(42) assert exc.job_pid == 42 @@ -121,8 +129,6 @@ def test_command_backgrounded_exception(): def test_command_cancelled_exception(): """Verify CommandCancelled carries its job_pid attribute.""" - from recline.commands.async_command import CommandCancelled - exc = CommandCancelled(99) assert exc.job_pid == 99 @@ -159,8 +165,6 @@ async def test_coro(): def test_async_command_foreground_killed(): """Verify that foreground() raises CommandCancelled when the thread was killed.""" - from recline.commands.async_command import CommandCancelled - i_was_started = False @recline.command(name="test") @@ -190,9 +194,6 @@ def test_async_command_foreground_backgrounded(): to the background while foreground() is waiting. """ - import time - from recline.commands.async_command import CommandBackgrounded - ready = [False] @recline.command(name="test") @@ -211,7 +212,6 @@ def do_background(): time.sleep(0.05) thread.background() - import threading t = threading.Thread(target=do_background) t.start() @@ -227,9 +227,6 @@ def do_background(): def test_set_terminal_echo_no_termios(monkeypatch): """Verify set_terminal_echo gracefully yields when termios is unavailable (e.g. Windows).""" - import sys - from recline.commands.async_command import set_terminal_echo - # Setting termios to None in sys.modules causes 'import termios' to raise ImportError monkeypatch.setitem(sys.modules, "termios", None) @@ -242,11 +239,6 @@ def test_set_terminal_echo_no_termios(monkeypatch): def test_set_terminal_echo_with_tty(monkeypatch): """Verify set_terminal_echo modifies echo when stdin is a real TTY.""" - import os - import sys - import termios - from recline.commands.async_command import set_terminal_echo - fake_attrs = [0, 0, 0, termios.ECHO, 0, 0, [b'\x00'] * 19] set_calls = [] @@ -265,11 +257,6 @@ def test_set_terminal_echo_with_tty(monkeypatch): def test_set_terminal_echo_enabled_true(monkeypatch): """Verify set_terminal_echo correctly enables ECHO (covering the enabled=True branch).""" - import os - import sys - import termios - from recline.commands.async_command import set_terminal_echo - # Start with ECHO disabled (bit not set) fake_attrs = [0, 0, 0, 0, 0, 0, [b'\x00'] * 19] set_calls = [] @@ -288,10 +275,6 @@ def test_set_terminal_echo_enabled_true(monkeypatch): def test_set_terminal_echo_non_tty(monkeypatch): """Verify set_terminal_echo yields without changes when stdin is not a TTY.""" - import os - import sys - from recline.commands.async_command import set_terminal_echo - monkeypatch.setattr(sys.stdin, "fileno", lambda: 0) monkeypatch.setattr(os, "isatty", lambda fd: False) diff --git a/tests/test_commands/test_builtin_commands.py b/tests/test_commands/test_builtin_commands.py index 339659a..bf0b022 100644 --- a/tests/test_commands/test_builtin_commands.py +++ b/tests/test_commands/test_builtin_commands.py @@ -4,12 +4,13 @@ A test module for the recline.commands.builtin_commands module """ +import curses import pdb import pudb import pytest import recline -from recline.commands import builtin_commands +from recline.commands import ReclineCommandError, builtin_commands from recline.commands.cli_command import CLICommand @@ -124,8 +125,6 @@ def test_man_commands_returns_non_aliases(): def test_exit_command_with_jobs_decline(monkeypatch): """Verify exit_command() with backgrounded jobs does NOT exit if user declines.""" - import recline - monkeypatch.setattr(recline, "JOBS", {1: object()}) monkeypatch.setattr("builtins.input", lambda _: "n") # Should return without raising SystemExit @@ -135,8 +134,6 @@ def test_exit_command_with_jobs_decline(monkeypatch): def test_exit_command_with_jobs_accept(monkeypatch): """Verify exit_command() with backgrounded jobs exits when user confirms.""" - import recline - class _FakeJob: def stop(self, dont_delete=False): pass @@ -151,8 +148,6 @@ def stop(self, dont_delete=False): def test_exit_command_abort_jobs(monkeypatch): """Verify exit_command() with abort_jobs=True cleans up jobs and exits.""" - import recline - stopped = [] class _FakeJob: @@ -168,9 +163,6 @@ def stop(self, dont_delete=False): def test_fg_no_jobs(monkeypatch): """Verify fg() raises ReclineCommandError when there are no jobs.""" - import recline - from recline.commands import ReclineCommandError - monkeypatch.setattr(recline, "JOBS", {}) with pytest.raises(ReclineCommandError, match="No running jobs"): builtin_commands.fg() @@ -179,9 +171,6 @@ def test_fg_no_jobs(monkeypatch): def test_fg_invalid_job(monkeypatch): """Verify fg() raises ReclineCommandError for an unknown job ID.""" - import recline - from recline.commands import ReclineCommandError - monkeypatch.setattr(recline, "JOBS", {1: object()}) with pytest.raises(ReclineCommandError, match="Could not find"): builtin_commands.fg(job=999) @@ -190,8 +179,6 @@ def test_fg_invalid_job(monkeypatch): def test_fg_with_job_no_formatter(monkeypatch): """Verify fg() completes silently when the job's command has no output formatter.""" - import recline - class _FakeCommand: output_formatter = None @@ -241,8 +228,6 @@ def test_debug_interrupt_handler_callable(monkeypatch): def test_fg_with_valid_job(monkeypatch): """Verify fg() calls foreground() and formats output for a known job.""" - import recline - formatted = [] class _FakeFormatter: @@ -274,8 +259,6 @@ def test_man_unknown_command(capsys): def test_man_command_large_window(monkeypatch): """Test man command with a large window so all text fits and (END) is displayed.""" - import curses - @recline.command(name="man large cmd") def _man_large(): """Short description for large-window man test.""" @@ -313,8 +296,6 @@ def getch(self): def test_man_command_scrolling(monkeypatch): """Test man command with a small window to exercise KEY_DOWN and KEY_UP scrolling.""" - import curses - @recline.command(name="man scroll cmd") def _man_scroll(): """Short desc for scroll test. diff --git a/tests/test_repl/test_shell.py b/tests/test_repl/test_shell.py index da61287..7bcda26 100644 --- a/tests/test_repl/test_shell.py +++ b/tests/test_repl/test_shell.py @@ -6,11 +6,19 @@ import asyncio import builtins +import readline +import sys +from typing import Annotated import pytest import recline +from recline.commands import ReclineCommandError, builtin_commands as bc +from recline.commands.async_command import CommandCancelled +from recline.commands.cli_command import CLICommand +from recline.formatters.output_formatter import OutputFormatter from recline.repl import shell +from recline.repl.shell import _split_unquoted @pytest.mark.parametrize("user_input, expected_marker, expected_output", [ @@ -153,8 +161,6 @@ def single_cmd_one_shot(): def test_relax_uses_sys_argv_when_none(monkeypatch): """Verify that relax() uses sys.argv when no argv is provided (covers the argv=None branch).""" - import sys - @recline.command(name="single command sys argv") def single_cmd(): return 45 @@ -167,8 +173,6 @@ def single_cmd(): def test_relax_repl_loop_empty_input(monkeypatch): """Verify that entering an empty string in the REPL loop is silently skipped.""" - import builtins - calls = [0] def mock_input(prompt): @@ -186,8 +190,6 @@ def mock_input(prompt): def test_relax_repl_loop_executes_command(monkeypatch): """Verify that a valid command entered in the REPL loop gets executed.""" - import builtins - ran = [False] calls = [0] @@ -210,8 +212,6 @@ def mock_input(prompt): def test_relax_repl_loop_keyboard_interrupt(monkeypatch, capsys): """Verify that KeyboardInterrupt in the REPL loop prints ^C and continues.""" - import builtins - calls = [0] def mock_input(prompt): @@ -231,9 +231,6 @@ def mock_input(prompt): def test_relax_repl_loop_debug_interrupt(monkeypatch, capsys): """Verify that DebugInterrupt is caught and debug() is invoked.""" - import builtins - from recline.commands import builtin_commands as bc - debug_called = [False] calls = [0] @@ -262,8 +259,6 @@ def mock_debug(): def test_split_unquoted_escaped_separator(): """Verify that _split_unquoted treats a backslash-escaped separator as literal.""" - from recline.repl.shell import _split_unquoted - # The \; should NOT be treated as a separator result = _split_unquoted("a \\; b", ";") assert result == ["a \\; b"] @@ -272,8 +267,6 @@ def test_split_unquoted_escaped_separator(): def test_split_unquoted_single_quoted_separator(): """Verify that _split_unquoted ignores separators inside single quotes.""" - from recline.repl.shell import _split_unquoted - result = _split_unquoted("a ';' b", ";") assert result == ["a ';' b"] @@ -281,8 +274,6 @@ def test_split_unquoted_single_quoted_separator(): def test_split_unquoted_double_quoted_separator(): """Verify that _split_unquoted ignores separators inside double quotes.""" - from recline.repl.shell import _split_unquoted - result = _split_unquoted('a ";" b', ";") assert result == ['a ";" b'] @@ -312,9 +303,6 @@ def ut_help_command(arg: int): def test_run_one_command_with_output_formatter(capsys): """Verify that when a command has an output_formatter, it is called and 0 is returned.""" - from recline.formatters.output_formatter import OutputFormatter - from typing import Annotated - class _FmtFormatter(OutputFormatter): def format_output(self, results): print(f"formatted:{results}") @@ -332,8 +320,6 @@ def fmt_command() -> Annotated[str, _FmtFormatter]: def test_run_one_command_backgrounded(capsys): """Verify that CommandBackgrounded is caught and reports the job correctly.""" - import asyncio - @recline.command(name="bg async command") async def bg_async(): await asyncio.sleep(10) @@ -350,8 +336,6 @@ async def bg_async(): def test_run_one_command_recline_command_error(capsys): """Verify that ReclineCommandError from a command is reported and returns 1.""" - from recline.commands import ReclineCommandError - @recline.command(name="err command") def err_command(): raise ReclineCommandError("intended error") @@ -365,9 +349,6 @@ def err_command(): def test_run_one_command_command_cancelled(monkeypatch): """Verify that CommandCancelled from a command is silently swallowed (returns 1).""" - from recline.commands.async_command import CommandCancelled - from recline.commands.cli_command import CLICommand - monkeypatch.setitem( recline.commands.COMMAND_REGISTRY, "__cancel_test", CLICommand(lambda: None, name="__cancel_test") ) @@ -383,13 +364,11 @@ def _raise_cancelled(cmd, args): def test_track_command_history_happy_path(monkeypatch): """Verify track_command_history reads history and sets length when file is readable.""" - import readline as _rl - read_called = [False] length_set = [None] - monkeypatch.setattr(_rl, "read_history_file", lambda f: read_called.__setitem__(0, True)) - monkeypatch.setattr(_rl, "set_history_length", lambda n: length_set.__setitem__(0, n)) + monkeypatch.setattr(readline, "read_history_file", lambda f: read_called.__setitem__(0, True)) + monkeypatch.setattr(readline, "set_history_length", lambda n: length_set.__setitem__(0, n)) shell.track_command_history("/fake/path/history") assert read_called[0] @@ -399,10 +378,6 @@ def test_track_command_history_happy_path(monkeypatch): def test_run_one_command_reraises_exit_command_code(): """Verify that a SystemExit with EXIT_COMMAND_CODE is re-raised.""" - import sys - from recline.commands import builtin_commands as bc - from recline.commands.cli_command import CLICommand - def _do_exit(): sys.exit(bc.EXIT_COMMAND_CODE) @@ -416,8 +391,6 @@ def _do_exit(): def test_setup_repl_with_program_name_prompt_history(monkeypatch, tmp_path): """Verify that _setup_repl respects program_name, prompt, and history_file overrides.""" - import builtins - history_file = str(tmp_path / "history.txt") def mock_eof(prompt): @@ -438,8 +411,6 @@ def mock_eof(prompt): def test_setup_repl_registers_exit_command(monkeypatch): """Verify that an atexit command is registered in _setup_repl.""" - import builtins - @recline.command(name="at exit cmd", atexit=True) def at_exit_cmd(): pass @@ -457,9 +428,6 @@ def mock_eof(prompt): def test_setup_repl_start_command_backgrounded(monkeypatch): """Verify that a start command that gets backgrounded is silently handled.""" - import asyncio - import builtins - @recline.command(name="bg startup", atstart=True, background=True) async def bg_startup(): await asyncio.sleep(10) @@ -496,8 +464,6 @@ def test_track_command_history_existing_file(tmp_path): def test_setup_tab_complete_gnu_readline(monkeypatch): """Verify setup_tab_complete works with GNU readline (no libedit in __doc__).""" - import readline - monkeypatch.setattr(readline, "__doc__", "GNU Readline library -- line editing support") # Should not raise shell.setup_tab_complete()