summaryrefslogtreecommitdiff
path: root/src/ninep/cols.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/ninep/cols.zig')
-rw-r--r--src/ninep/cols.zig70
1 files changed, 68 insertions, 2 deletions
diff --git a/src/ninep/cols.zig b/src/ninep/cols.zig
index 454cce4a..c4ee83eb 100644
--- a/src/ninep/cols.zig
+++ b/src/ninep/cols.zig
@@ -163,7 +163,7 @@ pub fn writeCtl(p: *Pardes, req: Req, serial: u32) Reply {
var lines = std.mem.splitScalar(u8, req.data, '\n');
while (lines.next()) |raw| {
const line = std.mem.trim(u8, raw, " \t\r");
- if (line.len == 0) continue;
+ if (line.len == 0 or pardes.exec.isComment(line)) continue;
const word = line[0 .. std.mem.indexOfAny(u8, line, " \t") orelse line.len];
const takes = for ([_][]const u8{ "Delcol", "Joincol", "New", "Tty" }) |w| {
if (std.mem.eql(u8, w, word)) break true;
@@ -228,6 +228,69 @@ test "New written to a column's exec makes its scratch in the session's director
try testing.expectEqualStrings("/project/src/+New", p.panes[p.active].?.file.?.path);
}
+test "pane/new takes rows from a pane in another column before refusing ENOSPC" {
+ const p = try pardes.Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 160, .rows = 16 });
+ defer p.deinit();
+ while (p.nextEffect()) |_| {}
+ const first = p.panes[p.active].?.serial;
+ // A second column holding one tall pane.
+ try testing.expectEqual(tree.Status.ok, th.wr(p, @intFromEnum(tree.TopFile.ctl), "Newcol\n").reply.status);
+ const tall = try th.newPane(p);
+ const tall_col = layout.findPane(p, p.paneBySerial(tall).?).?.col;
+ const focus = @intFromEnum(tree.TopFile.focus);
+ var digits: [16]u8 = undefined;
+ // From the first column, until pane/new is refused.
+ var moved = false;
+ for (0..pardes.MAX_PANES) |_| {
+ _ = th.wr(p, focus, try std.fmt.bufPrint(&digits, "{d}\n", .{first}));
+ const made = th.newPane(p) catch break;
+ if (layout.findPane(p, p.paneBySerial(made).?).?.col == tall_col) moved = true;
+ }
+ try testing.expect(moved);
+}
+
+test "a Newcol refused because the tags would not fit logs its err alone and spends no column serial" {
+ const p = try pardes.Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 160, .rows = 14 });
+ defer p.deinit();
+ while (p.nextEffect()) |_| {}
+ // Long tags wrap onto more rows as their column narrows: the boot's
+ // two panes share one, and each keeps its tag and two rows.
+ const made = try th.newPane(p);
+ const pane = p.panes[p.paneBySerial(made).?].?;
+ const col = layout.findPane(p, p.paneBySerial(made).?).?.col;
+ for (p.col_panes[col][0..p.col_n[col]]) |k| {
+ const each = p.panes[k].?;
+ if (each.tag.own) |old| p.gpa.free(old);
+ each.tag.own = try p.gpa.dupe(u8, "word " ** 60);
+ }
+ try testing.expect(p.col_n[col] >= 2);
+ p.sync();
+ const ctl = @intFromEnum(tree.TopFile.ctl);
+ const focus = @intFromEnum(tree.TopFile.focus);
+ var digits: [16]u8 = undefined;
+ const own = try std.fmt.bufPrint(&digits, "{d}\n", .{pane.serial});
+ var refused = false;
+ for (0..pardes.MAX_COLS) |_| {
+ // From the long tag's column, so it is the one split narrower.
+ _ = th.wr(p, focus, own);
+ const serial_before = p.next_column_serial;
+ const log_before = try pardes.ctlfs.events.freezeLog(p);
+ defer p.gpa.free(log_before.bytes);
+ const r = th.wr(p, ctl, "Newcol\n");
+ if (r.reply.status == .ok) continue;
+ if (std.mem.indexOf(u8, r.reply.ename, "tags would not fit") == null) continue;
+ refused = true;
+ try testing.expectEqual(serial_before, p.next_column_serial);
+ const log_after = try pardes.ctlfs.events.freezeLog(p);
+ defer p.gpa.free(log_after.bytes);
+ const added = log_after.bytes[log_before.bytes.len..];
+ try testing.expect(std.mem.startsWith(u8, added, "err "));
+ try testing.expectEqual(@as(usize, 1), std.mem.count(u8, added, "\n"));
+ break;
+ }
+ try testing.expect(refused);
+}
+
test "a tagexec word that makes, acts on and focuses no pane reads back nothing" {
const p = try th.withFile(testing.allocator, "x\n");
defer p.deinit();
@@ -458,7 +521,10 @@ test "a refused write after a truncation leaves every kind of tag as it was, and
const h = th.call(p, .{ .tag = 2, .op = .open, .node = node, .omode = 1 }).reply.handle;
const r = th.call(p, .{ .tag = 3, .op = .write, .node = node, .handle = h, .data = big });
try testing.expectEqual(E.NOSPC, r.errno());
- try testing.expectEqualStrings("tag: no space: over 4096 bytes", r.reply.ename);
+ try testing.expectEqualStrings("no space: over 4096 bytes", r.reply.ename);
+ // Its err record names the file once: `tag: no space`, never `tag: tag:`.
+ try testing.expect(th.logHas(p, "tag: no space: over 4096 bytes"));
+ try testing.expect(!th.logHas(p, "tag: tag:"));
_ = th.call(p, .{ .tag = 4, .op = .release, .node = node, .handle = h, .opened = true });
try testing.expectEqualStrings(before, th.rd(p, node, 0, 1 << 16).bytes);
// A control character is refused the same way on every kind.