summaryrefslogtreecommitdiff
path: root/tests.zig
diff options
context:
space:
mode:
Diffstat (limited to 'tests.zig')
-rw-r--r--tests.zig138
1 files changed, 84 insertions, 54 deletions
diff --git a/tests.zig b/tests.zig
index 1d5e8c4c..eeb397fa 100644
--- a/tests.zig
+++ b/tests.zig
@@ -59,6 +59,29 @@ fn pollFor(
return std.mem.indexOf(u8, output.items, needle) != null;
}
+fn nowMs() i64 {
+ var ts: linux.timespec = undefined;
+ _ = linux.clock_gettime(linux.CLOCK.MONOTONIC, &ts);
+ return ts.sec * 1000 + @divFloor(@as(i64, ts.nsec), 1_000_000);
+}
+
+// Drain the pty for a fixed WALL-CLOCK duration (a real settle). pollFor's
+// iter-count "settle" is unreliable for big bursts: reading caps at 4096/iter so
+// all iters can be spent reading with no idle wait, making it finish too soon.
+fn drainFor(gpa: std.mem.Allocator, master: c_int, output: *std.ArrayList(u8), ms: i64) !void {
+ const deadline = nowMs() + ms;
+ var buf: [4096]u8 = undefined;
+ while (nowMs() < deadline) {
+ var fds = [_]posix.pollfd{.{ .fd = master, .events = posix.POLL.IN, .revents = 0 }};
+ _ = posix.poll(&fds, 50) catch {};
+ if ((fds[0].revents & posix.POLL.IN) != 0) {
+ const n = posix.read(master, &buf) catch break;
+ if (n == 0) break;
+ try output.appendSlice(gpa, buf[0..n]);
+ }
+ }
+}
+
fn dump(items: []const u8, msg: []const u8) void {
std.debug.print("E2E FAIL: {s}\ncaptured {d} bytes:\n{s}\n", .{
msg,
@@ -112,61 +135,66 @@ pub fn main(init: std.process.Init) !void {
return error.ResizeNotDelivered;
}
- // 3. Sym mode (default): keystrokes are forwarded to the embedded shell.
- // The tag shows the "sy" mode indicator and the marker echoes back.
+ // NOTE on polls: vaxis diff-renders, and char-mode text is built one keystroke
+ // per frame, so inserted chars are emitted across frames and never appear as a
+ // contiguous byte run. We `settle` (drain so input is processed, also avoiding
+ // a resize race), force a full repaint with setWinsize (re-emits whole rows
+ // contiguously), then poll. Command *output* lands in one frame already.
+
+ // 3. Default is TEXT mode: the tag shows "ch" and shell prompts are hidden.
+ if (!try pollFor(gpa, master, &output, "ch /", 60)) {
+ dump(output.items, "default tag is not 'ch' (text mode)");
+ return error.TextModeNotDefault;
+ }
+
+ // 4. Esc switches to TTY mode and forwards keystrokes to the shell. The settle
+ // lets vaxis emit a *standalone* Escape (lone ESC + text would be Alt+char).
const marker = "PARDES_E2E";
+ try writeAll(master, "\x1b");
+ _ = try pollFor(gpa, master, &output, "ZZNEVERZZ", 6); // settle
try writeAll(master, "echo " ++ marker ++ "\r");
if (!try pollFor(gpa, master, &output, marker, 80)) {
- dump(output.items, "app did not render echoed marker");
- return error.MarkerNotRendered;
+ dump(output.items, "tty mode did not forward the marker to the shell");
+ return error.TtyModeNoForward;
}
- if (!try pollFor(gpa, master, &output, "sy ", 20)) {
- dump(output.items, "no 'sy' mode indicator on the tag in sym mode");
- return error.ModeIndicatorMissing;
+ try setWinsize(master, 50, 201); // repaint so the whole tag re-emits contiguously
+ if (!try pollFor(gpa, master, &output, "sy /", 40)) {
+ dump(output.items, "no 'sy' indicator after Esc (tty mode)");
+ return error.TtyModeIndicatorMissing;
}
- // NOTE on the polls below: vaxis diff-renders, and char-mode text is built
- // one keystroke per frame, so the inserted chars are emitted across frames
- // and never appear as a contiguous byte run. We therefore `settle` (drain so
- // the input is fully processed, which also avoids racing the resize), force a
- // full repaint with setWinsize (which re-emits whole rows contiguously), and
- // only then poll. Command *output* (e.g. R42Q) lands in one frame already, so
- // it needs no repaint.
-
- // 4. A left-click enters char mode and typed text is inserted as an overlay.
- // SGR mouse: press+release of button 0 at a body cell (col 6, row 3).
- try writeAll(master, "\x1b[<0;6;3M\x1b[<0;6;3m");
- try writeAll(master, "EDITZZ1");
+ // 5. Prompt hiding. In tty mode a command line + its output are both visible.
+ // Switching back to text mode hides the prompt/command rows but keeps the
+ // output. We clear the capture, switch with a left-click, force a repaint,
+ // then check the fresh frame: output present, prompt + command absent.
+ try writeAll(master, "echo CMDHIDEZZ\r");
+ if (!try pollFor(gpa, master, &output, "CMDHIDEZZ", 80)) {
+ dump(output.items, "tty mode did not run the command");
+ return error.TtyCommandFailed;
+ }
+ _ = try pollFor(gpa, master, &output, "ZZNEVERZZ", 8); // drain all tty-mode frames
+ output.clearRetainingCapacity(); // so the next poll only sees the text-mode repaint
+ try writeAll(master, "\x1b[<0;6;3M\x1b[<0;6;3m"); // left-click body -> text mode
_ = try pollFor(gpa, master, &output, "ZZNEVERZZ", 6); // settle
- try setWinsize(master, 50, 201); // force a full repaint
- if (!try pollFor(gpa, master, &output, "EDITZZ1", 80)) {
- dump(output.items, "char-mode insertion did not render");
- return error.EditNotRendered;
+ try setWinsize(master, 50, 202); // force a full repaint (text mode, prompts hidden)
+ if (!try pollFor(gpa, master, &output, "CMDHIDEZZ", 80)) {
+ dump(output.items, "command output not shown in text mode");
+ return error.OutputHiddenInTextMode;
}
-
- // 5. The same repaint confirms the mode flipped sy -> ch on the tag. If char
- // mode never engaged, the marker went to the shell and the tag reads "sy".
- if (!try pollFor(gpa, master, &output, "ch /", 80)) {
- dump(output.items, "left-click did not switch the tag to 'ch' (char mode)");
- return error.CharModeNotEntered;
+ if (std.mem.indexOf(u8, output.items, "goblin@") != null) {
+ dump(output.items, "shell prompt still visible in text mode");
+ return error.PromptNotHidden;
}
-
- // 6. A second click anchors a second independent insertion run.
- try writeAll(master, "\x1b[<0;6;5M\x1b[<0;6;5m");
- try writeAll(master, "EDITZZ2");
- _ = try pollFor(gpa, master, &output, "ZZNEVERZZ", 6); // settle
- try setWinsize(master, 50, 202); // force a full repaint
- if (!try pollFor(gpa, master, &output, "EDITZZ2", 80)) {
- dump(output.items, "second anchored insertion did not render");
- return error.SecondEditNotRendered;
+ if (std.mem.indexOf(u8, output.items, "echo CMDHIDEZZ") != null) {
+ dump(output.items, "command line still visible in text mode");
+ return error.CommandNotHidden;
}
- // 6b. Middle-click executes the typed text read off the rendered screen.
- // Char-click a blank body row, type a computed echo as an insertion, then
- // middle-select exactly those 15 cells (text col 0 -> screen cols 2..16)
- // and release. The result "Q7Z" appears only if the SELECTED inserted
- // text ran — proving selection sees typed text, not the tty beneath it.
- try writeAll(master, "\x1b[<0;3;14M\x1b[<0;3;14m"); // char-click blank row 13
+ // 6. Text-mode editing + middle-click execute. Click a blank row to pin the
+ // cursor at text col 0, compose a computed echo, then middle-select those
+ // 15 cells (screen cols 2..16) and release. "Q7Z" appears only if the
+ // SELECTED typed text ran — proving selection reads the rendered screen.
+ try writeAll(master, "\x1b[<0;3;14M\x1b[<0;3;14m"); // click blank row 13, col 0
try writeAll(master, "echo Q$((3+4))Z");
_ = try pollFor(gpa, master, &output, "ZZNEVERZZ", 6); // settle (build the run)
try writeAll(master, "\x1b[<1;3;14M\x1b[<33;17;14M\x1b[<1;17;14m"); // middle-select it
@@ -175,17 +203,19 @@ pub fn main(init: std.process.Init) !void {
return error.MiddleExecFailed;
}
- // 7. Esc leaves char mode (tag back to sy) and input resumes going to the
- // pty: a computed echo's result only appears if the shell executed it.
- // (In char mode the literal "$((20+22))" would render but never "R42Q".)
- // The settle after Esc lets vaxis emit a *standalone* Escape — a lone ESC
- // immediately followed by text is otherwise parsed as Alt+<char>.
+ // 7. Right-click a directory opens (acme "look") a terminal there and ls's
+ // it. Make a dir with a unique entry, then `clear` and echo its path so the
+ // path lands as contiguous output on body row 0 (screen row 1). Right-click
+ // (no drag) on it: the new terminal lists the dir, so the unique entry
+ // appears only if the directory was actually opened.
try writeAll(master, "\x1b");
- _ = try pollFor(gpa, master, &output, "ZZNEVERZZ", 6); // settle
- try writeAll(master, "echo R$((20+22))Q\r");
- if (!try pollFor(gpa, master, &output, "R42Q", 80)) {
- dump(output.items, "Esc did not return to sym mode (computed echo never ran)");
- return error.SymModeNotRestored;
+ try drainFor(gpa, master, &output, 700); // settle (standalone Esc -> tty)
+ try writeAll(master, "rm -rf /tmp/pardesE2E; mkdir -p /tmp/pardesE2E/ZZUNIQDIR; clear; echo /tmp/pardesE2E\r");
+ try drainFor(gpa, master, &output, 2000); // let it run + the screen fully settle
+ try writeAll(master, "\x1b[<2;5;2M\x1b[<2;5;2m"); // right-click the path on screen row 1
+ if (!try pollFor(gpa, master, &output, "ZZUNIQDIR", 120)) {
+ dump(output.items, "right-click on a directory did not open+ls it");
+ return error.RightClickDirFailed;
}
// Tell the app to quit (ctrl-c is the app's own exit key).