diff --git a/packages/testing/src/execution_testing/cli/eest/cli.py b/packages/testing/src/execution_testing/cli/eest/cli.py index b93469a5cb..70b5cdc288 100644 --- a/packages/testing/src/execution_testing/cli/eest/cli.py +++ b/packages/testing/src/execution_testing/cli/eest/cli.py @@ -3,12 +3,34 @@ Invoke using `uv run eest`. """ +import sys + import click from .commands import clean, info from .make.cli import make +def ensure_utf8_output() -> None: + """ + Reconfigure the standard streams to UTF-8 so output cannot crash. + + The `eest` commands print Unicode characters (box drawing, emoji) + that a legacy console code page such as Windows `cp1252` cannot + encode, otherwise raising `UnicodeEncodeError` mid-command. Streams + that do not support reconfiguration (for example when output is + captured in tests) are left untouched. + """ + for stream in (sys.stdout, sys.stderr): + reconfigure = getattr(stream, "reconfigure", None) + if reconfigure is None: + continue + try: + reconfigure(encoding="utf-8") + except (OSError, ValueError): + pass + + @click.group( context_settings={ "help_option_names": ["-h", "--help"], @@ -17,7 +39,7 @@ ) def eest() -> None: """`eest` is a CLI tool that helps with routine tasks.""" - pass + ensure_utf8_output() """ diff --git a/packages/testing/src/execution_testing/cli/eest/tests/__init__.py b/packages/testing/src/execution_testing/cli/eest/tests/__init__.py new file mode 100644 index 0000000000..a3645c910f --- /dev/null +++ b/packages/testing/src/execution_testing/cli/eest/tests/__init__.py @@ -0,0 +1 @@ +"""Test cases for the `eest` CLI group.""" diff --git a/packages/testing/src/execution_testing/cli/eest/tests/test_cli.py b/packages/testing/src/execution_testing/cli/eest/tests/test_cli.py new file mode 100644 index 0000000000..90d3ec8041 --- /dev/null +++ b/packages/testing/src/execution_testing/cli/eest/tests/test_cli.py @@ -0,0 +1,47 @@ +"""Tests for the `eest` CLI group.""" + +import io +import sys + +import pytest +from click.testing import CliRunner + +from ..cli import eest, ensure_utf8_output + + +def test_info_runs_successfully() -> None: + """`eest info` exits cleanly and reports the EEST banner.""" + result = CliRunner().invoke(eest, ["info"]) + assert result.exit_code == 0 + assert "EEST" in result.output + + +def test_info_survives_legacy_console_encoding( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """ + `eest info` must not crash on a non-UTF-8 console code page. + + Regression test for the Windows `cp1252` console, whose codec + cannot encode the box-drawing characters printed by the command. + """ + stream = io.TextIOWrapper(io.BytesIO(), encoding="cp1252") + monkeypatch.setattr(sys, "stdout", stream) + + # Without the UTF-8 reconfiguration this raises UnicodeEncodeError. + eest.main(["info"], standalone_mode=False) + + stream.flush() + assert "EEST" in stream.buffer.getvalue().decode("utf-8") + + +def test_ensure_utf8_output_reconfigures_stream( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """`ensure_utf8_output` switches a legacy stream to UTF-8.""" + stream = io.TextIOWrapper(io.BytesIO(), encoding="cp1252") + monkeypatch.setattr(sys, "stdout", stream) + + ensure_utf8_output() + + assert stream.encoding.lower() == "utf-8"