diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 08:00:41 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:16 -0300 |
| commit | ba0a0f82be43dde325012b3018b186bc808bdafc (patch) | |
| tree | b191c664840c11f8ab3a47c380a6a5f569ddb9b5 /test/panes.zig | |
| parent | 0b359e1f99f1a2630969c1c00b6c71db3b794ccd (diff) | |
| download | pardes-ba0a0f82be43dde325012b3018b186bc808bdafc.tar.gz pardes-ba0a0f82be43dde325012b3018b186bc808bdafc.zip | |
A Save whose write fails changes nothing: not the name, not the dirty flag
A scratch took its new name before the host wrote it, so `Save /root/x.txt`
failing with EACCES renamed it anyway; a failed write to another name
marked a clean file dirty. A scratch is now named once its write is done
(promoteSaved), and a failed write puts the saved revision back.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'test/panes.zig')
| -rw-r--r-- | test/panes.zig | 39 |
1 files changed, 33 insertions, 6 deletions
diff --git a/test/panes.zig b/test/panes.zig index 3d2821aa..1988fe16 100644 --- a/test/panes.zig +++ b/test/panes.zig @@ -2500,21 +2500,48 @@ test "Save on a scratch does not watch a path the host could not create" { const id = p.active; while (p.nextEffect()) |_| {} p.host = .{ .ctx = p, .vtable = &.{ .write_file = Refusing.write } }; + const file = &p.panes[id].?.file.?; + const scratch_name = try std.testing.allocator.dupe(u8, file.path); + defer std.testing.allocator.free(scratch_name); + const was_dirty = file.revision != file.saved_revision; try std.testing.expect(p.executeBuiltinLine(id, "Save /new-file.txt")); while (p.nextEffect()) |effect| p.perform(effect); - const file = &p.panes[id].?.file.?; - try std.testing.expect(file.output == null); - try std.testing.expect(file.watch_after_save); - try std.testing.expect(file.revision != file.saved_revision); + // Failed, it changed nothing: still the scratch, its name and dirty + // flag as they were, unwatched. + try std.testing.expect(file.output != null); + try std.testing.expectEqualStrings(scratch_name, file.path); + try std.testing.expectEqual(was_dirty, file.revision != file.saved_revision); try std.testing.expect(!p.fallback.watched[id]); p.host = .{}; - try std.testing.expect(p.executeBuiltinLine(id, "Save")); + try std.testing.expect(p.executeBuiltinLine(id, "Save /new-file.txt")); while (p.nextEffect()) |effect| p.perform(effect); - try std.testing.expect(!file.watch_after_save); + try std.testing.expect(file.output == null); + try std.testing.expectEqualStrings("/new-file.txt", file.path); try std.testing.expectEqual(file.revision, file.saved_revision); try std.testing.expect(p.fallback.watched[id]); } +test "a failed Save to another name leaves a clean named file clean" { + const Refusing = struct { + fn write(ctx: ?*anyopaque, id: u8, _: []const u8, _: []const u8) void { + const p: *Pardes = @ptrCast(@alignCast(ctx.?)); + p.saveFailed(id, "save", error.AccessDenied); + } + }; + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 40, .rows = 12 }); + defer p.deinit(); + const pane = try p.setTestFile("clean\n"); + while (p.nextEffect()) |_| {} + const file = &pane.file.?; + try std.testing.expectEqual(file.revision, file.saved_revision); + p.host = .{ .ctx = p, .vtable = &.{ .write_file = Refusing.write } }; + try std.testing.expect(p.executeBuiltinLine(0, "Save /root/x.txt")); + while (p.nextEffect()) |effect| p.perform(effect); + try std.testing.expectEqual(file.revision, file.saved_revision); + try std.testing.expectEqualStrings("/test.txt", file.path); + p.host = .{}; +} + test "unplaced terminal panes keep their allocated grid until placement" { const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 100, .rows = 30 }); defer p.deinit(); |
