mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
vt: add tests for write_pty and bell effect callbacks
Test that the write_pty callback receives correct DECRQM response data and userdata, that queries are silently ignored without a callback, and that setting null clears the callback. Test that the bell callback fires on single and multiple BEL characters with correct userdata, and that BEL without a callback is safe.
This commit is contained in:
@@ -864,6 +864,157 @@ test "grid_ref null terminal" {
|
||||
}, &out_ref));
|
||||
}
|
||||
|
||||
test "set write_pty callback" {
|
||||
var t: Terminal = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
&lib_alloc.test_allocator,
|
||||
&t,
|
||||
.{
|
||||
.cols = 80,
|
||||
.rows = 24,
|
||||
.max_scrollback = 0,
|
||||
},
|
||||
));
|
||||
defer free(t);
|
||||
|
||||
const S = struct {
|
||||
var last_data: ?[]u8 = null;
|
||||
var last_userdata: ?*anyopaque = null;
|
||||
|
||||
fn deinit() void {
|
||||
if (last_data) |d| testing.allocator.free(d);
|
||||
last_data = null;
|
||||
last_userdata = null;
|
||||
}
|
||||
|
||||
fn writePty(_: Terminal, ud: ?*anyopaque, ptr: [*]const u8, len: usize) callconv(.c) void {
|
||||
if (last_data) |d| testing.allocator.free(d);
|
||||
last_data = testing.allocator.dupe(u8, ptr[0..len]) catch @panic("OOM");
|
||||
last_userdata = ud;
|
||||
}
|
||||
};
|
||||
defer S.deinit();
|
||||
|
||||
// Set userdata and write_pty callback
|
||||
var sentinel: u8 = 42;
|
||||
const ud: ?*anyopaque = @ptrCast(&sentinel);
|
||||
set(t, .userdata, @ptrCast(&ud));
|
||||
const cb: ?Effects.WritePtyFn = &S.writePty;
|
||||
set(t, .write_pty, @ptrCast(&cb));
|
||||
|
||||
// DECRQM for wraparound mode (mode 7, set by default) should trigger write_pty
|
||||
vt_write(t, "\x1B[?7$p", 6);
|
||||
try testing.expect(S.last_data != null);
|
||||
try testing.expectEqualStrings("\x1B[?7;1$y", S.last_data.?);
|
||||
try testing.expectEqual(@as(?*anyopaque, @ptrCast(&sentinel)), S.last_userdata);
|
||||
}
|
||||
|
||||
test "set write_pty without callback ignores queries" {
|
||||
var t: Terminal = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
&lib_alloc.test_allocator,
|
||||
&t,
|
||||
.{
|
||||
.cols = 80,
|
||||
.rows = 24,
|
||||
.max_scrollback = 0,
|
||||
},
|
||||
));
|
||||
defer free(t);
|
||||
|
||||
// Without setting a callback, DECRQM should be silently ignored (no crash)
|
||||
vt_write(t, "\x1B[?7$p", 6);
|
||||
}
|
||||
|
||||
test "set write_pty null clears callback" {
|
||||
var t: Terminal = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
&lib_alloc.test_allocator,
|
||||
&t,
|
||||
.{
|
||||
.cols = 80,
|
||||
.rows = 24,
|
||||
.max_scrollback = 0,
|
||||
},
|
||||
));
|
||||
defer free(t);
|
||||
|
||||
const S = struct {
|
||||
var called: bool = false;
|
||||
fn writePty(_: Terminal, _: ?*anyopaque, _: [*]const u8, _: usize) callconv(.c) void {
|
||||
called = true;
|
||||
}
|
||||
};
|
||||
S.called = false;
|
||||
|
||||
// Set then clear the callback
|
||||
const cb: ?Effects.WritePtyFn = &S.writePty;
|
||||
set(t, .write_pty, @ptrCast(&cb));
|
||||
set(t, .write_pty, null);
|
||||
|
||||
vt_write(t, "\x1B[?7$p", 6);
|
||||
try testing.expect(!S.called);
|
||||
}
|
||||
|
||||
test "set bell callback" {
|
||||
var t: Terminal = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
&lib_alloc.test_allocator,
|
||||
&t,
|
||||
.{
|
||||
.cols = 80,
|
||||
.rows = 24,
|
||||
.max_scrollback = 0,
|
||||
},
|
||||
));
|
||||
defer free(t);
|
||||
|
||||
const S = struct {
|
||||
var bell_count: usize = 0;
|
||||
var last_userdata: ?*anyopaque = null;
|
||||
|
||||
fn bell(_: Terminal, ud: ?*anyopaque) callconv(.c) void {
|
||||
bell_count += 1;
|
||||
last_userdata = ud;
|
||||
}
|
||||
};
|
||||
S.bell_count = 0;
|
||||
S.last_userdata = null;
|
||||
|
||||
// Set userdata and bell callback
|
||||
var sentinel: u8 = 99;
|
||||
const ud: ?*anyopaque = @ptrCast(&sentinel);
|
||||
set(t, .userdata, @ptrCast(&ud));
|
||||
const cb: ?Effects.BellFn = &S.bell;
|
||||
set(t, .bell, @ptrCast(&cb));
|
||||
|
||||
// Single BEL
|
||||
vt_write(t, "\x07", 1);
|
||||
try testing.expectEqual(@as(usize, 1), S.bell_count);
|
||||
try testing.expectEqual(@as(?*anyopaque, @ptrCast(&sentinel)), S.last_userdata);
|
||||
|
||||
// Multiple BELs
|
||||
vt_write(t, "\x07\x07", 2);
|
||||
try testing.expectEqual(@as(usize, 3), S.bell_count);
|
||||
}
|
||||
|
||||
test "bell without callback is silent" {
|
||||
var t: Terminal = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
&lib_alloc.test_allocator,
|
||||
&t,
|
||||
.{
|
||||
.cols = 80,
|
||||
.rows = 24,
|
||||
.max_scrollback = 0,
|
||||
},
|
||||
));
|
||||
defer free(t);
|
||||
|
||||
// BEL without a callback should not crash
|
||||
vt_write(t, "\x07", 1);
|
||||
}
|
||||
|
||||
test "grid_ref out of bounds" {
|
||||
var t: Terminal = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
|
||||
Reference in New Issue
Block a user