1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
from contextlib import contextmanager
from itertools import repeat
from types import SimpleNamespace
from typing import Iterator
from unittest.mock import patch
from pytest import raises
from rich.console import Console
from rich.measure import Measurement
from syrupy.assertion import SnapshotAssertion
from tests.data import CONSOLE_OPTIONS, TEST_IMAGE
from tests.utils import load_non_seekable_bytes_io, render
from textual_image._terminal import TerminalError
def test_render(snapshot: SnapshotAssertion) -> None:
from textual_image.renderable.tgp import Image
renderable = Image(TEST_IMAGE, width=4)
with patch.object(Image, "_image_id_counter", repeat(1337)): # Encoded in diacritics, so it has to be fixed
assert render(renderable).startswith("\x1b_Gi=1337,m=1,f=100,q=2;")
def test_overly_large() -> None:
from textual_image.renderable.tgp import Image
renderable = Image(TEST_IMAGE, width=2**32)
with raises(ValueError):
render(renderable)
def test_render_non_seekable() -> None:
from textual_image.renderable.tgp import Image
test_image = load_non_seekable_bytes_io(TEST_IMAGE)
renderable = Image(test_image)
assert test_image.read() == b""
# encoding is not deterministic, so we don't compare the output
render(renderable)
render(renderable)
test_image.close()
render(renderable)
def test_measure() -> None:
from textual_image.renderable.tgp import Image
renderable = Image(TEST_IMAGE, width=4)
assert renderable.__rich_measure__(Console(), CONSOLE_OPTIONS) == Measurement(4, 4)
def test_cleanup() -> None:
from textual_image.renderable.tgp import Image
renderable = Image(TEST_IMAGE, width=4)
with patch("textual_image.renderable.tgp._send_tgp_message") as send_terminal_graphics_protocol_message:
renderable.cleanup()
assert not send_terminal_graphics_protocol_message.called
with patch("textual_image.renderable.tgp._send_tgp_message") as send_terminal_graphics_protocol_message:
render(renderable)
renderable.cleanup()
assert send_terminal_graphics_protocol_message.called
def test_send_tgp_message() -> None:
from textual_image.renderable.tgp import _send_tgp_message
with patch("sys.__stdout__", None):
with raises(TerminalError):
_send_tgp_message(d=42)
with patch("sys.__stdout__") as stdout:
_send_tgp_message(d=42)
assert stdout.write.call_args[0][0] == "\x1b_Gd=42\x1b\\"
assert stdout.flush.called
def test_query_terminal_support() -> None:
from textual_image.renderable.tgp import _TGP_MESSAGE_END, _TGP_MESSAGE_START, query_terminal_support
@contextmanager
def response_success(start_marker: str, end_marker: str, timeout: float | None = None) -> Iterator[SimpleNamespace]:
yield SimpleNamespace(sequence=f"{_TGP_MESSAGE_START}d=1;OK{_TGP_MESSAGE_END}")
@contextmanager
def response_failure(start_marker: str, end_marker: str, timeout: float | None = None) -> Iterator[SimpleNamespace]:
yield SimpleNamespace(sequence=f"{_TGP_MESSAGE_START}d=1;FAIL{_TGP_MESSAGE_END}")
@contextmanager
def response_exception(
start_marker: str, end_marker: str, timeout: float | None = None
) -> Iterator[SimpleNamespace]:
raise TimeoutError()
with patch("textual_image.renderable.tgp.capture_terminal_response", response_success):
with patch("textual_image.renderable.tgp._send_tgp_message"):
assert query_terminal_support()
with patch("textual_image.renderable.tgp.capture_terminal_response", response_failure):
with patch("textual_image.renderable.tgp._send_tgp_message"):
assert not query_terminal_support()
with patch("textual_image.renderable.tgp.capture_terminal_response", response_exception):
with patch("textual_image.renderable.tgp._send_tgp_message"):
assert not query_terminal_support()
|