summaryrefslogtreecommitdiff
path: root/tests.zig
diff options
context:
space:
mode:
Diffstat (limited to 'tests.zig')
-rw-r--r--tests.zig110
1 files changed, 110 insertions, 0 deletions
diff --git a/tests.zig b/tests.zig
index 9bb97f22..83e42dad 100644
--- a/tests.zig
+++ b/tests.zig
@@ -26,6 +26,16 @@ fn rowOf(text: []const u8, needle: []const u8) ?usize {
return null;
}
+// Column (0-based byte offset within its line) of the first `needle` on screen.
+// The grid is monospace ASCII here, so the byte offset is the screen column.
+fn colOf(text: []const u8, needle: []const u8) ?usize {
+ var it = std.mem.splitScalar(u8, text, '\n');
+ while (it.next()) |line| {
+ if (std.mem.indexOf(u8, line, needle)) |off| return off;
+ }
+ return null;
+}
+
pub fn main(init: std.process.Init) !void {
const gpa = init.gpa;
const args = try init.minimal.args.toSlice(init.arena.allocator());
@@ -479,4 +489,104 @@ pub fn main(init: std.process.Init) !void {
// grapheme slice into a stack-local cell).
try hs5.expectContains("█", "petscii image body is blank — no block glyphs rendered");
}
+
+ // 16. Opening a SECOND file SPLITS the existing file pane (acme-style minimal
+ // reflow) instead of stacking at the top of the column: the new file lands
+ // directly BELOW the source file, so A is above B. The OLD stack-at-top
+ // behavior would put B ABOVE A — so asserting rowOf(A) < rowOf(B) is a
+ // regression test for the split.
+ {
+ var hs6 = try Harness.init(gpa, exe.ptr, 24, 80);
+ defer hs6.deinit();
+ try hs6.expectWaitFor("build.zig", 8000, "fresh terminal never auto-ls'd");
+ try h.writeFile("/tmp/pardesE2Efa.txt", "ZZFILEA\nfa1\nfa2\nfa3\nfa4\nfa5\nfa6\nfa7\nfa8\nfa9\n");
+ try h.writeFile("/tmp/pardesE2Efb.txt", "ZZFILEB\nfb1\nfb2\nfb3\nfb4\nfb5\nfb6\nfb7\nfb8\nfb9\n");
+ try hs6.resize(30, 100); // clean geometry: right column starts ~col 51
+ try hs6.pump(700);
+ // open file A from the right-column terminal: the FIRST doc pane, so it
+ // takes the stack-at-top fallback (no existing doc pane to split).
+ try hs6.send("\x1b[<0;60;20M\x1b[<0;60;20m"); // pin a blank row, right column -> normal
+ try hs6.pump(400);
+ try hs6.send("i");
+ try hs6.pump(300);
+ try hs6.send("/tmp/pardesE2Efa.txt");
+ try hs6.pump(500);
+ try hs6.send("\x1b"); // -> normal
+ try hs6.pump(400);
+ try hs6.send("\x1b[<2;70;20M\x1b[<2;70;20m"); // right-click the path -> open file A
+ try hs6.expectWaitFor("ZZFILEA", 12000, "right-click did not open file A");
+ // open file B likewise: a doc pane now exists, so B SPLITS it (lands below A).
+ try hs6.send("\x1b[<0;60;22M\x1b[<0;60;22m"); // pin another blank row, right column
+ try hs6.pump(400);
+ try hs6.send("i");
+ try hs6.pump(300);
+ try hs6.send("/tmp/pardesE2Efb.txt");
+ try hs6.pump(500);
+ try hs6.send("\x1b");
+ try hs6.pump(400);
+ try hs6.send("\x1b[<2;70;22M\x1b[<2;70;22m"); // right-click the path -> open file B
+ try hs6.expectWaitFor("ZZFILEB", 12000, "right-click did not open file B");
+ // both markers sit on each file's TOP line (scroll 0). The split puts B
+ // under A -> A's row < B's row. Stack-at-top would invert this.
+ const text = try hs6.screenText();
+ defer gpa.free(text);
+ const ra = rowOf(text, "ZZFILEA") orelse return error.NoFileAOnScreen;
+ const rb = rowOf(text, "ZZFILEB") orelse return error.NoFileBOnScreen;
+ if (!(ra < rb)) {
+ hs6.dump("second file did not split BELOW the first (B not under A)");
+ return error.FileSplitOrderWrong;
+ }
+ }
+
+ // 17. Deleting a pane that EMPTIES its column hands the column's width to a
+ // neighbor, so the OTHER columns keep their widths (only the neighbor
+ // grows). Regression for the extra horizontal movement: without the
+ // col-weight handoff, wsum drops and every surviving column reflows
+ // sideways. Build 3 columns, put a marker in the rightmost, delete the
+ // MIDDLE column's shell (exit), and assert the marker's screen column is
+ // unchanged (the middle column's width flowed LEFT, not to the right one).
+ {
+ var hs7 = try Harness.init(gpa, exe.ptr, 24, 80);
+ defer hs7.deinit();
+ try hs7.expectWaitFor("build.zig", 8000, "fresh terminal never auto-ls'd");
+ try hs7.resize(30, 120); // 3 columns of ~40 each
+ try hs7.pump(700);
+ // add a 3rd column via the global-tagbar "Newcol" (cols 5-10, 0-based).
+ try hs7.send("\x1b[<1;8;1M\x1b[<1;8;1m"); // middle-click "Newcol"
+ try hs7.pump(1600); // let the new shell come up
+ // mark the rightmost column (now the active pane) with a unique token.
+ try hs7.send("\x02"); // normal -> tty
+ try hs7.pump(500);
+ try hs7.send("clear\r");
+ try hs7.pump(800);
+ // split the token so it appears ONLY in the OUTPUT (at the pane's left edge),
+ // never in the command text — else colOf would match the prompt-relative echo.
+ try hs7.send("echo ZZFAR''COL\r");
+ try hs7.expectWaitFor("ZZFARCOL", 8000, "new column's shell did not echo the marker");
+ const before = try hs7.screenText();
+ const x1 = colOf(before, "ZZFARCOL") orelse {
+ gpa.free(before);
+ return error.NoFarColBefore;
+ };
+ gpa.free(before);
+ // focus the MIDDLE column (x 40..80) and exit its shell -> the column
+ // empties and collapses. pty_eof deletes the pane and reflows.
+ try hs7.send("\x1b[<0;58;12M\x1b[<0;58;12m"); // left-click inside the middle column
+ try hs7.pump(500);
+ try hs7.send("\x02"); // -> tty
+ try hs7.pump(400);
+ try hs7.send("exit\r"); // shell EOF -> pane removed -> middle column collapses
+ try hs7.pump(2000); // let pty_eof + the reflow settle
+ try hs7.expectContains("ZZFARCOL", "marker vanished after collapsing the middle column");
+ const after = try hs7.screenText();
+ defer gpa.free(after);
+ const x2 = colOf(after, "ZZFARCOL") orelse return error.NoFarColAfter;
+ // with the width handoff the rightmost column stays put (x2 ~= x1); without
+ // it, every column reflows and x2 shrinks by ~one column width.
+ if (x2 + 2 < x1) {
+ hs7.dump("rightmost column moved when the middle column collapsed (no width handoff)");
+ std.debug.print("ZZFARCOL col: before={d} after={d}\n", .{ x1, x2 });
+ return error.ColumnCollapseReflow;
+ }
+ }
}