diff --git a/cli/src/zshell/subcommands/__pycache__/scheme.cpython-314.pyc b/cli/src/zshell/subcommands/__pycache__/scheme.cpython-314.pyc index e553570..06584ac 100644 Binary files a/cli/src/zshell/subcommands/__pycache__/scheme.cpython-314.pyc and b/cli/src/zshell/subcommands/__pycache__/scheme.cpython-314.pyc differ diff --git a/cli/src/zshell/subcommands/__pycache__/screenshot.cpython-314.pyc b/cli/src/zshell/subcommands/__pycache__/screenshot.cpython-314.pyc index ecd3450..f1921ea 100644 Binary files a/cli/src/zshell/subcommands/__pycache__/screenshot.cpython-314.pyc and b/cli/src/zshell/subcommands/__pycache__/screenshot.cpython-314.pyc differ diff --git a/cli/src/zshell/subcommands/__pycache__/shell.cpython-314.pyc b/cli/src/zshell/subcommands/__pycache__/shell.cpython-314.pyc index a337671..d6ca509 100644 Binary files a/cli/src/zshell/subcommands/__pycache__/shell.cpython-314.pyc and b/cli/src/zshell/subcommands/__pycache__/shell.cpython-314.pyc differ diff --git a/cli/src/zshell/subcommands/__pycache__/wallpaper.cpython-314.pyc b/cli/src/zshell/subcommands/__pycache__/wallpaper.cpython-314.pyc index c888649..37f69d4 100644 Binary files a/cli/src/zshell/subcommands/__pycache__/wallpaper.cpython-314.pyc and b/cli/src/zshell/subcommands/__pycache__/wallpaper.cpython-314.pyc differ diff --git a/cli/src/zshell/subcommands/shell.py b/cli/src/zshell/subcommands/shell.py index d66ea57..de8ca3b 100644 --- a/cli/src/zshell/subcommands/shell.py +++ b/cli/src/zshell/subcommands/shell.py @@ -18,29 +18,32 @@ def kill(): sys.stderr.write(result.stderr.decode()) +def start_instance(no_daemon: bool = False) -> None: + result = subprocess.run(args + ["-n"] + ([] if no_daemon else ["-d"]), capture_output=True) + stdout = result.stdout.decode().strip() + if stdout: + if "already running" in stdout.lower(): + raise click.ClickException(stdout) + if result.returncode != 0: + stderr = result.stderr.decode().strip() + raise click.ClickException(stderr) + + @app.command() def start(no_daemon: bool = False): - check = subprocess.run(args + ["ipc"] + ["show"], capture_output=True) - if check.returncode == 0: - raise click.ClickException("An instance of this configuration is already running.") - result = subprocess.run(args + ["-n"] + ([] if no_daemon else ["-d"]), capture_output=True) - if result.returncode != 0: - raise click.ClickException(result.stderr.decode().strip()) - sys.stderr.write(result.stderr.decode()) + start_instance(no_daemon) @app.command() def restart(no_daemon: bool = False): - subprocess.run(args + ["kill"]) - for _ in range(50): + subprocess.run(args + ["kill"], capture_output=True) + deadline = time.monotonic() + 2.5 + while time.monotonic() < deadline: result = subprocess.run(args + ["kill"], capture_output=True) if result.returncode == 255: break time.sleep(0.05) - result = subprocess.run(args + ["-n"] + ([] if no_daemon else ["-d"]), capture_output=True) - if result.returncode != 0: - raise click.ClickException(result.stderr.decode().strip()) - sys.stderr.write(result.stderr.decode()) + start_instance(no_daemon=no_daemon) @app.command() diff --git a/cli/tests/test_shell.py b/cli/tests/test_shell.py index 9875d99..1763247 100644 --- a/cli/tests/test_shell.py +++ b/cli/tests/test_shell.py @@ -34,35 +34,30 @@ class TestKill: class TestStart: @patch("zshell.subcommands.shell.subprocess.run") def test_start_default_daemon(self, mock_run): - mock_run.side_effect = [ - CompletedProcess([], 1, b"", b""), # ipc show → no instance - CompletedProcess([], 0, b"", b"Launching config\n"), # launch ok - ] + mock_run.return_value = CompletedProcess([], 0, b"", b"Launching config\n") invoke("start") - assert mock_run.call_args_list == [ - call(["qs", "-c", "zshell", "ipc", "show"], capture_output=True), - call(["qs", "-c", "zshell", "-n", "-d"], capture_output=True), - ] + mock_run.assert_called_once_with(["qs", "-c", "zshell", "-n", "-d"], capture_output=True) @patch("zshell.subcommands.shell.subprocess.run") def test_start_no_daemon(self, mock_run): - mock_run.side_effect = [ - CompletedProcess([], 1, b"", b""), - CompletedProcess([], 0, b"", b"Launching config\n"), - ] + mock_run.return_value = CompletedProcess([], 0, b"", b"Launching config\n") invoke("start", "--no-daemon") - assert mock_run.call_args_list == [ - call(["qs", "-c", "zshell", "ipc", "show"], capture_output=True), - call(["qs", "-c", "zshell", "-n"], capture_output=True), - ] + mock_run.assert_called_once_with(["qs", "-c", "zshell", "-n"], capture_output=True) @patch("zshell.subcommands.shell.subprocess.run") def test_start_already_running_errors(self, mock_run): - mock_run.return_value = CompletedProcess([], 0, b"", b"target visibilities\n") + mock_run.return_value = CompletedProcess([], 0, b"An instance of this configuration is already running.\n", b"") result = runner.invoke(app, ["start"]) assert result.exit_code != 0 assert "already running" in result.output + @patch("zshell.subcommands.shell.subprocess.run") + def test_start_other_failure_errors(self, mock_run): + mock_run.return_value = CompletedProcess([], 1, b"", b"Config error\n") + result = runner.invoke(app, ["start"]) + assert result.exit_code != 0 + assert "Config error" in result.output + class TestShow: @patch("zshell.subcommands.shell.subprocess.run") @@ -106,30 +101,30 @@ class TestCall: class TestRestart: + @patch("zshell.subcommands.shell.start_instance") @patch("zshell.subcommands.shell.subprocess.run") - def test_restart_kills_then_starts(self, mock_run): + def test_restart_kills_then_starts(self, mock_run, mock_start): mock_run.side_effect = [ - CompletedProcess([], 0, b"", b"Killed abc\n"), # first kill (no capture) + CompletedProcess([], 0, b"", b"Killed abc\n"), # first kill (captured) CompletedProcess([], 255, b"", b""), # poll → no instance - CompletedProcess([], 0, b"", b"Launching config\n"), # launch ok ] invoke("restart") assert mock_run.call_args_list == [ - call(["qs", "-c", "zshell", "kill"]), # no capture_output call(["qs", "-c", "zshell", "kill"], capture_output=True), - call(["qs", "-c", "zshell", "-n", "-d"], capture_output=True), + call(["qs", "-c", "zshell", "kill"], capture_output=True), ] + mock_start.assert_called_once_with(no_daemon=False) + @patch("zshell.subcommands.shell.start_instance") @patch("zshell.subcommands.shell.subprocess.run") - def test_restart_no_daemon(self, mock_run): + def test_restart_no_daemon(self, mock_run, mock_start): mock_run.side_effect = [ CompletedProcess([], 0, b"", b"Killed abc\n"), CompletedProcess([], 255, b"", b""), - CompletedProcess([], 0, b"", b"Launching config\n"), ] invoke("restart", "--no-daemon") assert mock_run.call_args_list == [ - call(["qs", "-c", "zshell", "kill"]), call(["qs", "-c", "zshell", "kill"], capture_output=True), - call(["qs", "-c", "zshell", "-n"], capture_output=True), + call(["qs", "-c", "zshell", "kill"], capture_output=True), ] + mock_start.assert_called_once_with(no_daemon=True)