fix(conformance): wait for split shell startup before column probes
The resize fixture submitted the new pane's command before its shell produced initial output. Reuse the bounded boot precondition before the first sibling probe while preserving immediate post-resize measurements and strict geometry assertions. Cover delayed and silent startup with cleanup.
This commit is contained in:
1 parent
e0e180c2ac
commit
c8c951bcda
3 files changed
+118
-2
No files matched your search
@@ -75,6 +75,15 @@ def run(ctx: ConformanceContext) -> dict[str, object]:
|
||||
try:
|
||||
source_before = ctx.measure_columns(source)
|
||||
measurements["source_columns_before"] = source_before
|
||||
# Creating a pane precedes shell startup. Use the same initial-output
|
||||
# precondition as boot before sending the sibling's first command.
|
||||
# Measurements after pane.resize remain immediate and strict.
|
||||
ctx.poll(
|
||||
lambda: ctx.read(sibling),
|
||||
lambda value: bool(value.get("text", "").strip()),
|
||||
"split shell did not render initial output",
|
||||
timeout=ctx.startup_timeout,
|
||||
)
|
||||
sibling_before = ctx.measure_columns(sibling)
|
||||
measurements["sibling_columns_before"] = sibling_before
|
||||
resized = ctx.api(
|
||||
|
||||
@@ -14,7 +14,7 @@ if str(SCRIPTS_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(SCRIPTS_DIR))
|
||||
|
||||
from conformance.cases import resize
|
||||
from conformance.harness import ConformanceError
|
||||
from conformance.harness import ConformanceContext, ConformanceError
|
||||
|
||||
|
||||
class ResizeContext:
|
||||
@@ -23,15 +23,23 @@ class ResizeContext:
|
||||
shell = "bash"
|
||||
platform_family = "macos"
|
||||
|
||||
def __init__(self, artifact_dir: Path, columns: list[int]) -> None:
|
||||
def __init__(self, artifact_dir: Path, columns: list[int], outputs=None) -> None:
|
||||
self.artifact_dir = artifact_dir
|
||||
self.columns = iter(columns)
|
||||
self.cleanup: list[str] = []
|
||||
self.startup_timeout = 3.0
|
||||
self.outputs = list(outputs) if outputs is not None else ["fixture prompt"]
|
||||
self.sibling_ready = outputs is None
|
||||
self.read_count = 0
|
||||
self.measured_panes: list[int] = []
|
||||
self.resized = False
|
||||
self.poll = ConformanceContext.poll.__get__(self)
|
||||
|
||||
def api(self, method: str, params=None, **kwargs):
|
||||
if method == "pane.split":
|
||||
return {"action": {"pane_id": 11}}
|
||||
if method == "pane.resize":
|
||||
self.resized = True
|
||||
return {"action": {"ratio": params["ratio"]}}
|
||||
if method == "runtime.snapshot":
|
||||
return {"windows": [{"id": self.window_id}]}
|
||||
@@ -40,13 +48,46 @@ class ResizeContext:
|
||||
raise AssertionError(method)
|
||||
|
||||
def measure_columns(self, pane_id: int) -> int:
|
||||
if pane_id == 11 and not self.sibling_ready:
|
||||
raise AssertionError("columns probe sent before sibling startup")
|
||||
self.measured_panes.append(pane_id)
|
||||
return next(self.columns)
|
||||
|
||||
def read(self, pane_id: int):
|
||||
if self.resized:
|
||||
raise AssertionError("startup polling must not delay post-resize measurements")
|
||||
self.read_count += 1
|
||||
text = self.outputs[0]
|
||||
if len(self.outputs) > 1:
|
||||
self.outputs.pop(0)
|
||||
self.sibling_ready = bool(text.strip())
|
||||
return {"text": text}
|
||||
|
||||
def best_effort_api(self, method: str, params) -> None:
|
||||
self.cleanup.append(method)
|
||||
|
||||
|
||||
class ResizeDiagnosticsTests(unittest.TestCase):
|
||||
def test_cold_sibling_renders_output_before_its_first_columns_command(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
ctx = ResizeContext(Path(directory), [57, 56, 80, 33], ["", " \n", "PS C:\\fixture> "])
|
||||
with patch("time.sleep"), patch("time.monotonic", side_effect=[0, 0.1, 0.2, 0.3]):
|
||||
result = resize.run(ctx)
|
||||
self.assertTrue(result["pty_columns_changed"])
|
||||
self.assertEqual(ctx.read_count, 3)
|
||||
self.assertEqual(ctx.measured_panes, [10, 11, 10, 11])
|
||||
self.assertEqual(ctx.cleanup, ["pane.close", "window.focus"])
|
||||
|
||||
def test_silent_sibling_fails_before_resize_and_is_closed(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
ctx = ResizeContext(Path(directory), [57], [""])
|
||||
with patch("time.sleep"), patch("time.monotonic", side_effect=range(20)):
|
||||
with self.assertRaisesRegex(ConformanceError, "split shell did not render initial output"):
|
||||
resize.run(ctx)
|
||||
self.assertFalse(ctx.resized)
|
||||
self.assertEqual(ctx.measured_panes, [10])
|
||||
self.assertEqual(ctx.cleanup, ["pane.close", "window.focus"])
|
||||
|
||||
def test_growing_and_shrinking_panes_pass_without_diagnostic_sampling(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
ctx = ResizeContext(Path(directory), [57, 56, 80, 33])
|
||||
|
||||
Reference in new issue
Block a user