summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-07-31 10:37:19 -0300
committerGabriel Schneider <[email protected]>2026-08-01 15:02:08 -0300
commit0b7a480bef38b209741c520e2478d178767a9e51 (patch)
tree837b2222db111743cdbfc7f9706323009c330c2a /test
parent5cf16eab0a4beec196e51bdcee731b899a2af07c (diff)
downloadpardes-0b7a480bef38b209741c520e2478d178767a9e51.tar.gz
pardes-0b7a480bef38b209741c520e2478d178767a9e51.zip
the gui shell watches files too
It never did — the effect arm was `.watch => {}` with a comment saying a shell that never delivers the event simply never reloads. That was written as a deliberate scope cut and it is the whole bug: the user runs pardes-gui. The tty watcher was fine, and I proved all four suspicions false against a real binary in a real pty with the write done by a stranger process and NO input delivered afterwards: the loop does wake (postEvent signals an empty queue), zig fmt's rename-over does fire MOVED_TO and is caught, three panes across two directories all reload and closing one leaves its neighbour still following, and the self-write hash guard does not swallow a real change. The live session even had its inotify mark on src with the right mask and re-read config.zig when I touched that directory. Duplicated rather than shared with tty.zig, the way the two shells already each own LspJob, forkShell and their pty readers. The headless PARDES_TEST_GRID path deliberately passes -1: its contract is one frame per scripted input, and a reload on its own clock would put an unasked-for frame in the stream. filewatch.snap proved less than it looked. Its real blind spots were the rename-over shape — the old script only truncated, so it saw CLOSE_WRITE and never MOVED_TO — multiple directories, and the one that mattered: the suite only ever runs the TTY binary, so it structurally cannot see a gui-only regression. It now uses a new `run` directive whose writer is a child of the RUNNER, so nothing it does reaches an app pty.
Diffstat (limited to 'test')
-rw-r--r--test/snapshot.zig10
-rw-r--r--test/snapshots/filewatch.golden73
-rw-r--r--test/snapshots/filewatch.snap43
3 files changed, 93 insertions, 33 deletions
diff --git a/test/snapshot.zig b/test/snapshot.zig
index 4344654c..19b65a02 100644
--- a/test/snapshot.zig
+++ b/test/snapshot.zig
@@ -28,6 +28,7 @@
// file <name> <content> create file in the script's cwd (before start)
// lines <name> <n> [tail] create file with n numbered lines, tail on each
// dirmk <name> create a subdirectory
+// run <shell...> run a command in the script's cwd (NOT in a pane)
// start <rows> <cols> [arg] fork the app in a pty (optional extra CLI arg)
// wait <ms> <needle...> pump until needle appears on the grid (fails hard)
// settle <ms> pump for a fixed duration
@@ -62,6 +63,7 @@ pub const std_options: std.Options = .{ .log_level = .err };
extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int;
extern "c" fn execvp(file: [*:0]const u8, argv: [*:null]const ?[*:0]const u8) c_int;
+extern "c" fn system(cmd: [*:0]const u8) c_int;
const SNAP_BASE = "/tmp/pardes-snap";
var trace_stable = false;
@@ -379,6 +381,14 @@ fn runScript(arena: std.mem.Allocator, exe_z: [:0]const u8, script_path: []const
try eh.writeFile(try arena.dupeZ(u8, name), ppm.items);
} else if (std.mem.eql(u8, cmd, "dirmk")) {
try mkdir(arena, tok.next() orelse return error.BadScript, false);
+ } else if (std.mem.eql(u8, cmd, "run")) {
+ // A writer that is NOT a pardes pane. `file` mid-script is already
+ // one, but it cannot express the rename-over that is how a
+ // formatter actually replaces a file — and the difference is a
+ // different inotify event. This runs in the script's cwd, as a
+ // child of the RUNNER, so nothing it does reaches the app's ptys:
+ // whatever the app then shows, it woke up for by itself.
+ if (system(try arena.dupeZ(u8, tok.rest())) != 0) return error.RunFailed;
} else if (std.mem.eql(u8, cmd, "start")) {
const rows = try std.fmt.parseInt(u16, tok.next() orelse return error.BadScript, 10);
const cols = try std.fmt.parseInt(u16, tok.next() orelse return error.BadScript, 10);
diff --git a/test/snapshots/filewatch.golden b/test/snapshots/filewatch.golden
index 64c43c85..71a56bba 100644
--- a/test/snapshots/filewatch.golden
+++ b/test/snapshots/filewatch.golden
@@ -2,8 +2,8 @@
|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
| NOR /tmp/pardes-snap/filewatch/cwd/w.txt Save De NOR /tmp/pardes-snap/filewatch/cwd Del NOR /tmp/pardes-snap/filewatch/cwd Del
| 1 liMYEDITne 1
-| 2 line 2 w.txt w.txt
-| 3 line 3
+| 2 line 2 sub w.txt sub w.txt
+| 3 /tmp/pardes-snap/filewatch/cwd/sub/s.txt
| 4 line 4
| 5 line 5
| 6 line 6 w.txt
@@ -15,12 +15,29 @@
| 12 line 12
| 13
|
-| NOR /tmp/pardes-snap/filewatch/cwd Del
+| NOR /tmp/pardes-snap/filewatch/cwd/sub/s.txt Sav NOR /tmp/pardes-snap/filewatch/cwd Del
+| 1 line 1
+| 2 line 2 sub w.txt
+| 3 line 3
+| 4 line 4
+| 5 line 5
+| 6 line 6
+| 7 line 7
+| 8 line 8
+| 9
|
-| w.txt
|
|
|
+== snap reloaded grid=150x30 cursor=15,2
+|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
+| NOR /tmp/pardes-snap/filewatch/cwd/w.txt Save De NOR /tmp/pardes-snap/filewatch/cwd Del NOR /tmp/pardes-snap/filewatch/cwd Del
+| 1 EXTERNAL
+| 2 sub w.txt sub w.txt
+|
+|
+|
+| w.txt
|
|
|
@@ -29,16 +46,29 @@
|
|
|
-== snap reloaded grid=150x30 cursor=54,5
+| NOR /tmp/pardes-snap/filewatch/cwd/sub/s.txt Sav NOR /tmp/pardes-snap/filewatch/cwd Del
+| 1 line 1
+| 2 line 2 sub w.txt
+| 3 line 3
+| 4 line 4
+| 5 line 5
+| 6 line 6
+| 7 line 7
+| 8 line 8
+| 9
+|
+|
+|
+|
+== snap reloaded_sub grid=150x30 cursor=15,2
|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
-| NOR /tmp/pardes-snap/filewatch/cwd/w.txt Save De TTY /tmp/pardes-snap/filewatch/cwd Del NOR /tmp/pardes-snap/filewatch/cwd Del
-| 1 EXTERNAL $ ls
-| 2 w.txt w.txt
-| $ echo EXTERN''AL > w.txt
-| $
+| NOR /tmp/pardes-snap/filewatch/cwd/w.txt Save De NOR /tmp/pardes-snap/filewatch/cwd Del NOR /tmp/pardes-snap/filewatch/cwd Del
+| 1 EXTERNAL
+| 2 sub w.txt sub w.txt
|
|
|
+| w.txt
|
|
|
@@ -46,9 +76,10 @@
|
|
|
-| NOR /tmp/pardes-snap/filewatch/cwd Del
|
-| w.txt
+| NOR /tmp/pardes-snap/filewatch/cwd/sub/s.txt Sav NOR /tmp/pardes-snap/filewatch/cwd Del
+| 1 RENAMED
+| 2 sub w.txt
|
|
|
@@ -62,13 +93,13 @@
|
== snap undone grid=150x30 cursor=15,2
|Kill Newcol Tutor Debug NextColor Dump Find Grep Help
-| NOR /tmp/pardes-snap/filewatch/cwd/w.txt Save De TTY /tmp/pardes-snap/filewatch/cwd Del NOR /tmp/pardes-snap/filewatch/cwd Del
-| 1 liMYEDITne 1 $ ls
-| 2 line 2 w.txt w.txt
-| 3 line 3 $ echo EXTERN''AL > w.txt
-| 4 line 4 $
+| NOR /tmp/pardes-snap/filewatch/cwd/w.txt Save De NOR /tmp/pardes-snap/filewatch/cwd Del NOR /tmp/pardes-snap/filewatch/cwd Del
+| 1 liMYEDITne 1
+| 2 line 2 sub w.txt sub w.txt
+| 3 /tmp/pardes-snap/filewatch/cwd/sub/s.txt
+| 4 line 4
| 5 line 5
-| 6 line 6
+| 6 line 6 w.txt
| 7 line 7
| 8 line 8
| 9 line 9
@@ -77,9 +108,9 @@
| 12 line 12
| 13
|
-| NOR /tmp/pardes-snap/filewatch/cwd Del
-|
-| w.txt
+| NOR /tmp/pardes-snap/filewatch/cwd/sub/s.txt Sav NOR /tmp/pardes-snap/filewatch/cwd Del
+| 1 RENAMED
+| 2 sub w.txt
|
|
|
diff --git a/test/snapshots/filewatch.snap b/test/snapshots/filewatch.snap
index 1ac8d081..6f1f2976 100644
--- a/test/snapshots/filewatch.snap
+++ b/test/snapshots/filewatch.snap
@@ -1,7 +1,23 @@
# external file updates: a write from outside pardes lands in the open pane,
# and Undo brings the unsaved edit back. That is the whole contract — the
# update is committed like any other edit, so nothing has to be merged.
-lines w.txt 12
+#
+# The writer must be a STRANGER to the editor. This script used to have a
+# pardes PANE run the write, which proved much less than it looked: that
+# shell's own pty output wakes the loop and paints a frame, so the reload
+# could be riding on the keystroke's frame instead of on the watch. `run` is a
+# child of the RUNNER — nothing it does reaches an app pty — and every
+# assertion below it is a `wait`, which only READS the pty. So between the
+# write and the reload the app receives no keystroke, no resize, no mouse: if
+# the watch does not wake the loop by itself, these waits time out.
+#
+# Two files in two directories, written the two ways files actually change:
+# w.txt in place (CLOSE_WRITE) and sub/s.txt by rename-over, which is what
+# `zig fmt`, `git checkout` and every editor's atomic save do (MOVED_TO, and
+# a new inode — the reason the mark is on the directory).
+dirmk sub
+file w.txt line 1\nline 2\n/tmp/pardes-snap/filewatch/cwd/sub/s.txt\nline 4\nline 5\nline 6\nline 7\nline 8\nline 9\nline 10\nline 11\nline 12\n
+lines sub/s.txt 8
start 30 150 -n 3
wait 8000 w.txt
stable 700 20000
@@ -15,9 +31,15 @@ key esc
settle 100
press right 6 8
release right 6 8
-wait 10000 line 3
+wait 10000 line 4
stable 700 15000
-# an UNSAVED edit on line 1
+# ...and open sub/s.txt from the path sitting on line 3 of that pane, so the
+# second watched pane is in a DIFFERENT directory from the first
+press right 20 5
+release right 20 5
+wait 10000 line 8
+stable 700 15000
+# an UNSAVED edit on line 1 of w.txt
press left 10 3
release left 10 3
stable 400 5000
@@ -27,18 +49,15 @@ key esc
settle 100
stable 400 5000
snap edited
-# overwrite the file from a shell in another column. The marker is split so
-# the ECHOED command line never matches the wait — only the reloaded pane does
-press left 60 5
-release left 60 5
-stable 400 5000
-key c-b
-stable 600 8000
-text echo EXTERN''AL > w.txt
-key enter
+# ---- from here to `snap reloaded_sub`, pardes is sent NOTHING ----
+run printf 'EXTERNAL\n' > w.txt
wait 10000 EXTERNAL
stable 700 10000
snap reloaded
+run printf 'RENAMED\n' > sub/t && mv sub/t sub/s.txt
+wait 10000 RENAMED
+stable 700 10000
+snap reloaded_sub
# back in the file pane: one undo and the unsaved edit is there again
press left 10 3
release left 10 3