mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-08-28 10:01:38 +00:00
terminal/snapshot: test fixtures
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
//! | 8 | 2 | Version (`u16`) |
|
||||
|
||||
const std = @import("std");
|
||||
const test_fixture = @import("fixture.zig");
|
||||
const io = @import("io.zig");
|
||||
|
||||
/// Identifies a Ghostty terminal snapshot and rejects unrelated input before
|
||||
@@ -65,15 +66,25 @@ fn computeLen() usize {
|
||||
}
|
||||
}
|
||||
|
||||
const test_golden_fixture = test_fixture.parse(
|
||||
@embedFile("testdata/envelope-v1.hex"),
|
||||
);
|
||||
|
||||
test "golden encoding" {
|
||||
var buf: [encoded_len]u8 = undefined;
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
try encode(&writer);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
"GHOSTSNP\x01\x00",
|
||||
try test_fixture.expectEqual(
|
||||
.bytes,
|
||||
"src/terminal/snapshot/testdata/envelope-v1.hex",
|
||||
"snapshot_fixture-envelope-v1.hex",
|
||||
&test_golden_fixture,
|
||||
writer.buffered(),
|
||||
);
|
||||
|
||||
var reader: std.Io.Reader = .fixed(&test_golden_fixture);
|
||||
try decode(&reader);
|
||||
}
|
||||
|
||||
test "reject invalid magic and version" {
|
||||
@@ -88,9 +99,8 @@ test "reject invalid magic and version" {
|
||||
}
|
||||
|
||||
test "reject every truncation" {
|
||||
const fixture = "GHOSTSNP\x01\x00";
|
||||
for (0..encoded_len) |len| {
|
||||
var reader: std.Io.Reader = .fixed(fixture[0..len]);
|
||||
var reader: std.Io.Reader = .fixed(test_golden_fixture[0..len]);
|
||||
try std.testing.expectError(error.EndOfStream, decode(&reader));
|
||||
}
|
||||
}
|
||||
|
||||
416
src/terminal/snapshot/fixture.zig
Normal file
416
src/terminal/snapshot/fixture.zig
Normal file
@@ -0,0 +1,416 @@
|
||||
//! Test-only support for reviewable snapshot golden fixtures.
|
||||
//!
|
||||
//! Snapshot compatibility references are stored as annotated hexadecimal text
|
||||
//! under `testdata/`. Each byte is written as exactly two hexadecimal digits.
|
||||
//! Whitespace is insignificant, and `#` begins a comment through the end of the
|
||||
//! line. This permits field, record, and absolute-offset annotations without
|
||||
//! changing the bytes embedded by a test.
|
||||
//!
|
||||
//! `parse` validates and decodes an `@embedFile` at comptime. Tests should
|
||||
//! encode their native value, compare the generated bytes with that embedded
|
||||
//! reference using `expectEqual`, and then decode the reference itself. This
|
||||
//! keeps encoding regression coverage separate from backwards-decoding
|
||||
//! coverage.
|
||||
//!
|
||||
//! `expectEqual` formats a fresh candidate on every run. Matching candidates
|
||||
//! remain in the test's temporary directory. A mismatch copies the candidate
|
||||
//! to the repository root, reports its path and first differing byte, and
|
||||
//! fails without modifying the checked-in reference. After reviewing an
|
||||
//! intentional wire change, a maintainer manually replaces the reference.
|
||||
//! Fixture filenames include the snapshot version so a future format adds new
|
||||
//! references instead of overwriting compatibility data for an older version.
|
||||
//!
|
||||
//! A typical fixed-value test looks like this:
|
||||
//!
|
||||
//! ```zig
|
||||
//! const fixture = @import("fixture.zig");
|
||||
//!
|
||||
//! const reference = fixture.parse(
|
||||
//! @embedFile("testdata/example-v1.hex"),
|
||||
//! );
|
||||
//!
|
||||
//! test "example golden encoding and decoding" {
|
||||
//! var encoded: [Example.len]u8 = undefined;
|
||||
//! var writer: std.Io.Writer = .fixed(&encoded);
|
||||
//! try Example.encode(value, &writer);
|
||||
//!
|
||||
//! try fixture.expectEqual(
|
||||
//! .bytes,
|
||||
//! "src/terminal/snapshot/testdata/example-v1.hex",
|
||||
//! "snapshot_fixture-example-v1.hex",
|
||||
//! &reference,
|
||||
//! writer.buffered(),
|
||||
//! );
|
||||
//!
|
||||
//! var reader: std.Io.Reader = .fixed(&reference);
|
||||
//! try std.testing.expectEqualDeep(
|
||||
//! value,
|
||||
//! try Example.decode(&reader),
|
||||
//! );
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
const std = @import("std");
|
||||
const envelope = @import("envelope.zig");
|
||||
const record = @import("record.zig");
|
||||
|
||||
const log = std.log.scoped(.snapshot_fixture);
|
||||
|
||||
pub const Kind = enum {
|
||||
bytes,
|
||||
page,
|
||||
snapshot,
|
||||
};
|
||||
|
||||
/// Decode an annotated hexadecimal fixture at comptime.
|
||||
///
|
||||
/// Whitespace is ignored. `#` begins a comment that continues through the end
|
||||
/// of the line. Every other token must be exactly two hexadecimal digits.
|
||||
pub fn parse(comptime source: []const u8) [decodedLen(source)]u8 {
|
||||
comptime {
|
||||
// Large complete-snapshot fixtures exceed Zig's default comptime
|
||||
// branch budget while scanning their comments and byte tokens.
|
||||
@setEvalBranchQuota(100_000_000);
|
||||
|
||||
// The first pass in decodedLen gives this result its exact array type.
|
||||
// Keeping it an array lets callers use the fixture in comptime slices.
|
||||
var result: [decodedLen(source)]u8 = undefined;
|
||||
var source_index: usize = 0;
|
||||
var result_index: usize = 0;
|
||||
|
||||
while (source_index < source.len) {
|
||||
// Comments and layout are purely for reviewers and contribute no
|
||||
// bytes to the embedded reference.
|
||||
skipIgnored(source, &source_index);
|
||||
if (source_index == source.len) break;
|
||||
|
||||
// Every data token is one complete byte. Parse it directly rather
|
||||
// than accepting variable-width integers or other hex syntax.
|
||||
if (source_index + 1 >= source.len) {
|
||||
@compileError("snapshot fixture ends with one hex digit");
|
||||
}
|
||||
result[result_index] =
|
||||
hexNibble(source[source_index]) << 4 |
|
||||
hexNibble(source[source_index + 1]);
|
||||
result_index += 1;
|
||||
source_index += 2;
|
||||
|
||||
// Requiring a separator catches accidental third digits and makes
|
||||
// the accepted grammar match the formatter below.
|
||||
if (source_index < source.len and
|
||||
!std.ascii.isWhitespace(source[source_index]) and
|
||||
source[source_index] != '#')
|
||||
{
|
||||
@compileError(
|
||||
"snapshot fixture hex bytes must be separated by whitespace",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// Compare generated bytes with a checked-in reference.
|
||||
///
|
||||
/// A formatted candidate is always created in a temporary directory. On a
|
||||
/// mismatch it is copied into the repository root before the test fails.
|
||||
pub fn expectEqual(
|
||||
kind: Kind,
|
||||
reference_path: []const u8,
|
||||
candidate_name: []const u8,
|
||||
expected: []const u8,
|
||||
actual: []const u8,
|
||||
) !void {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var tmp = testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
// Always materialize a candidate, even when it will match. This keeps the
|
||||
// generation path exercised on every test run while avoiding repository
|
||||
// artifacts for the normal successful case.
|
||||
{
|
||||
var candidate_file = try tmp.dir.createFile(
|
||||
testing.io,
|
||||
candidate_name,
|
||||
.{},
|
||||
);
|
||||
defer candidate_file.close(testing.io);
|
||||
var write_buffer: [4096]u8 = undefined;
|
||||
var candidate_writer = candidate_file.writer(
|
||||
testing.io,
|
||||
&write_buffer,
|
||||
);
|
||||
try format(kind, actual, &candidate_writer.interface);
|
||||
try candidate_writer.interface.flush();
|
||||
}
|
||||
|
||||
// Matching candidates remain in the temporary directory and disappear
|
||||
// with its cleanup.
|
||||
if (std.mem.eql(u8, expected, actual)) return;
|
||||
|
||||
// The test temp directory is not necessarily below the working directory.
|
||||
// Resolve both paths before copying the candidate to the stable,
|
||||
// user-visible repository-root name.
|
||||
const tmp_path = try tmp.dir.realPathFileAlloc(
|
||||
testing.io,
|
||||
candidate_name,
|
||||
alloc,
|
||||
);
|
||||
defer alloc.free(tmp_path);
|
||||
const cwd = try std.Io.Dir.cwd().realPathFileAlloc(
|
||||
testing.io,
|
||||
".",
|
||||
alloc,
|
||||
);
|
||||
defer alloc.free(cwd);
|
||||
const candidate_path = try std.fs.path.join(
|
||||
alloc,
|
||||
&.{ cwd, candidate_name },
|
||||
);
|
||||
defer alloc.free(candidate_path);
|
||||
try std.Io.Dir.copyFileAbsolute(
|
||||
tmp_path,
|
||||
candidate_path,
|
||||
testing.io,
|
||||
.{},
|
||||
);
|
||||
|
||||
// Report the first useful comparison point. When one byte sequence is a
|
||||
// prefix of the other, this is the shared length where they diverge.
|
||||
const difference = difference: {
|
||||
const shared_len = @min(expected.len, actual.len);
|
||||
for (
|
||||
expected[0..shared_len],
|
||||
actual[0..shared_len],
|
||||
0..,
|
||||
) |expected_byte, actual_byte, index| {
|
||||
if (expected_byte != actual_byte) break :difference index;
|
||||
}
|
||||
break :difference shared_len;
|
||||
};
|
||||
log.err(
|
||||
"snapshot fixture differs reference={s} candidate={s} " ++
|
||||
"offset=0x{x} expected_len={} actual_len={}",
|
||||
.{
|
||||
reference_path,
|
||||
candidate_path,
|
||||
difference,
|
||||
expected.len,
|
||||
actual.len,
|
||||
},
|
||||
);
|
||||
return error.TestExpectedEqual;
|
||||
}
|
||||
|
||||
fn decodedLen(comptime source: []const u8) usize {
|
||||
comptime {
|
||||
// This is a complete validation pass, not just a count. Invalid syntax
|
||||
// therefore fails before parse allocates and fills its result array.
|
||||
@setEvalBranchQuota(100_000_000);
|
||||
|
||||
var index: usize = 0;
|
||||
var result: usize = 0;
|
||||
while (index < source.len) {
|
||||
skipIgnored(source, &index);
|
||||
if (index == source.len) break;
|
||||
|
||||
if (index + 1 >= source.len) {
|
||||
@compileError("snapshot fixture ends with one hex digit");
|
||||
}
|
||||
_ = hexNibble(source[index]);
|
||||
_ = hexNibble(source[index + 1]);
|
||||
result += 1;
|
||||
index += 2;
|
||||
|
||||
if (index < source.len and
|
||||
!std.ascii.isWhitespace(source[index]) and
|
||||
source[index] != '#')
|
||||
{
|
||||
@compileError(
|
||||
"snapshot fixture hex bytes must be separated by whitespace",
|
||||
);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
fn skipIgnored(comptime source: []const u8, index: *usize) void {
|
||||
while (index.* < source.len) {
|
||||
// Layout whitespace is unrestricted so fixtures can group fields and
|
||||
// keep deterministic sixteen-byte output lines.
|
||||
if (std.ascii.isWhitespace(source[index.*])) {
|
||||
index.* += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// A comment consumes the rest of its line. The newline itself is
|
||||
// consumed by the whitespace case on the next iteration.
|
||||
if (source[index.*] != '#') return;
|
||||
while (index.* < source.len and source[index.*] != '\n') {
|
||||
index.* += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn hexNibble(comptime value: u8) u8 {
|
||||
return switch (value) {
|
||||
'0'...'9' => value - '0',
|
||||
'a'...'f' => value - 'a' + 10,
|
||||
'A'...'F' => value - 'A' + 10,
|
||||
else => @compileError("snapshot fixture contains a non-hex digit"),
|
||||
};
|
||||
}
|
||||
|
||||
fn format(
|
||||
kind: Kind,
|
||||
bytes: []const u8,
|
||||
writer: *std.Io.Writer,
|
||||
) std.Io.Writer.Error!void {
|
||||
// Every generated candidate starts with enough maintenance context to be
|
||||
// understandable when copied out of a failing test's temp directory.
|
||||
try writer.print(
|
||||
"# Ghostty snapshot fixture\n" ++
|
||||
"# Wire version: {}\n" ++
|
||||
"# Generated by its snapshot test; review before replacing.\n" ++
|
||||
"# On mismatch, the candidate is copied to the repository root.\n",
|
||||
.{envelope.version},
|
||||
);
|
||||
|
||||
// Small values only need a hex dump. Larger compound formats get
|
||||
// structure-aware section labels without changing their decoded bytes.
|
||||
switch (kind) {
|
||||
.bytes => try writeSection(
|
||||
"encoded bytes",
|
||||
bytes,
|
||||
0,
|
||||
bytes.len,
|
||||
writer,
|
||||
),
|
||||
.page => try formatPage(bytes, writer),
|
||||
.snapshot => try formatSnapshot(bytes, writer),
|
||||
}
|
||||
}
|
||||
|
||||
fn formatPage(
|
||||
bytes: []const u8,
|
||||
writer: *std.Io.Writer,
|
||||
) std.Io.Writer.Error!void {
|
||||
// PAGE version 1 has a twenty-byte fixed header. The remainder is
|
||||
// self-describing through the counts and dimensions in that header.
|
||||
const header_end = @min(bytes.len, 20);
|
||||
try writeSection("PAGE header", bytes, 0, header_end, writer);
|
||||
if (header_end < bytes.len) {
|
||||
try writeSection(
|
||||
"style and hyperlink tables, rows, and cells",
|
||||
bytes,
|
||||
header_end,
|
||||
bytes.len,
|
||||
writer,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn formatSnapshot(
|
||||
bytes: []const u8,
|
||||
writer: *std.Io.Writer,
|
||||
) std.Io.Writer.Error!void {
|
||||
// A complete snapshot begins with its unframed envelope. Clamp this first
|
||||
// section so a partially generated candidate can still be inspected.
|
||||
const envelope_end = @min(bytes.len, envelope.encoded_len);
|
||||
try writeSection("snapshot envelope", bytes, 0, envelope_end, writer);
|
||||
|
||||
// Walk record framing only far enough to annotate boundaries. The emitted
|
||||
// hex remains authoritative; this formatter never repairs candidate data.
|
||||
var offset: usize = envelope_end;
|
||||
while (offset < bytes.len) {
|
||||
// A partial header cannot provide a safe payload boundary, so preserve
|
||||
// the remaining bytes as one explicitly incomplete section.
|
||||
if (bytes.len - offset < record.Header.len) {
|
||||
try writeSection(
|
||||
"trailing partial record",
|
||||
bytes,
|
||||
offset,
|
||||
bytes.len,
|
||||
writer,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Tag and payload length are the first six bytes of every record
|
||||
// header. CRC validation belongs to the codec test, not this formatter.
|
||||
const tag_raw = std.mem.readInt(
|
||||
u16,
|
||||
bytes[offset..][0..2],
|
||||
.little,
|
||||
);
|
||||
const payload_len: u32 = std.mem.readInt(
|
||||
u32,
|
||||
bytes[offset + 2 ..][0..4],
|
||||
.little,
|
||||
);
|
||||
|
||||
// Clamp the section to the available candidate bytes. This makes a
|
||||
// truncated payload reviewable instead of indexing beyond the slice.
|
||||
const record_end: usize = @min(
|
||||
bytes.len,
|
||||
offset + record.Header.len + @as(usize, payload_len),
|
||||
);
|
||||
|
||||
// Known tags make the annotated diff readable. Preserve unknown tags
|
||||
// numerically so formatter output is still useful for encoder bugs.
|
||||
if (std.enums.fromInt(record.Tag, tag_raw)) |tag| {
|
||||
try writer.print(
|
||||
"\n# offset 0x{x:0>8}: {s} record, payload {} bytes\n",
|
||||
.{ offset, @tagName(tag), payload_len },
|
||||
);
|
||||
} else {
|
||||
try writer.print(
|
||||
"\n# offset 0x{x:0>8}: unknown record {}, payload {} bytes\n",
|
||||
.{ offset, tag_raw, payload_len },
|
||||
);
|
||||
}
|
||||
try writeHex(bytes, offset, record_end, writer);
|
||||
offset = record_end;
|
||||
}
|
||||
}
|
||||
|
||||
fn writeSection(
|
||||
name: []const u8,
|
||||
bytes: []const u8,
|
||||
start: usize,
|
||||
end: usize,
|
||||
writer: *std.Io.Writer,
|
||||
) std.Io.Writer.Error!void {
|
||||
// Sections begin on a fresh line and carry their absolute wire offset.
|
||||
try writer.print(
|
||||
"\n# offset 0x{x:0>8}: {s}\n",
|
||||
.{ start, name },
|
||||
);
|
||||
try writeHex(bytes, start, end, writer);
|
||||
}
|
||||
|
||||
fn writeHex(
|
||||
bytes: []const u8,
|
||||
start: usize,
|
||||
end: usize,
|
||||
writer: *std.Io.Writer,
|
||||
) std.Io.Writer.Error!void {
|
||||
const digits = "0123456789abcdef";
|
||||
var offset = start;
|
||||
while (offset < end) {
|
||||
// Lowercase, sixteen-byte lines provide stable textual diffs. The
|
||||
// trailing offset is a comment and is ignored by parse.
|
||||
const line_end = @min(end, offset + 16);
|
||||
for (bytes[offset..line_end], 0..) |byte, index| {
|
||||
if (index != 0) try writer.writeByte(' ');
|
||||
try writer.writeByte(digits[byte >> 4]);
|
||||
try writer.writeByte(digits[byte & 0x0f]);
|
||||
}
|
||||
try writer.print(" # 0x{x:0>8}\n", .{offset});
|
||||
offset = line_end;
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,7 @@
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const test_fixture = @import("fixture.zig");
|
||||
const io = @import("io.zig");
|
||||
const page = @import("page.zig");
|
||||
const record = @import("record.zig");
|
||||
@@ -334,6 +335,10 @@ fn hasSemanticPrompt(terminal_page: *const TerminalPage) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
const test_header_fixture = test_fixture.parse(
|
||||
@embedFile("testdata/history-header-v1.hex"),
|
||||
);
|
||||
|
||||
test "HISTORY header golden encoding and decoding" {
|
||||
const expected: Header = .{
|
||||
.key = .alternate,
|
||||
@@ -341,20 +346,24 @@ test "HISTORY header golden encoding and decoding" {
|
||||
.total_rows = 0x0102030405060708,
|
||||
.screen_overlap_rows = 0x090a,
|
||||
};
|
||||
const fixture =
|
||||
"\x01\x00\x04\x03\x02\x01" ++
|
||||
"\x08\x07\x06\x05\x04\x03\x02\x01\x0a\x09";
|
||||
|
||||
try std.testing.expectEqual(Header.len, fixture.len);
|
||||
var encoded: [Header.len]u8 = undefined;
|
||||
var writer: std.Io.Writer = .fixed(&encoded);
|
||||
try expected.encode(&writer);
|
||||
try std.testing.expectEqualStrings(fixture, writer.buffered());
|
||||
try test_fixture.expectEqual(
|
||||
.bytes,
|
||||
"src/terminal/snapshot/testdata/history-header-v1.hex",
|
||||
"snapshot_fixture-history-header-v1.hex",
|
||||
&test_header_fixture,
|
||||
writer.buffered(),
|
||||
);
|
||||
try std.testing.expectEqual(Header.len, test_header_fixture.len);
|
||||
|
||||
var reader: std.Io.Reader = .fixed(fixture);
|
||||
var reader: std.Io.Reader = .fixed(&test_header_fixture);
|
||||
try std.testing.expectEqualDeep(expected, try Header.decode(&reader));
|
||||
for (0..Header.len) |fixture_len| {
|
||||
var truncated: std.Io.Reader = .fixed(fixture[0..fixture_len]);
|
||||
var truncated: std.Io.Reader = .fixed(
|
||||
test_header_fixture[0..fixture_len],
|
||||
);
|
||||
try std.testing.expectError(
|
||||
error.EndOfStream,
|
||||
Header.decode(&truncated),
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const test_fixture = @import("fixture.zig");
|
||||
const io = @import("io.zig");
|
||||
const terminal_hyperlink = @import("../hyperlink.zig");
|
||||
const terminal_page = @import("../page.zig");
|
||||
@@ -270,6 +271,14 @@ fn decodePageString(
|
||||
};
|
||||
}
|
||||
|
||||
const test_implicit_fixture = test_fixture.parse(
|
||||
@embedFile("testdata/hyperlink-implicit-v1.hex"),
|
||||
);
|
||||
|
||||
const test_explicit_fixture = test_fixture.parse(
|
||||
@embedFile("testdata/hyperlink-explicit-v1.hex"),
|
||||
);
|
||||
|
||||
test "golden implicit encoding" {
|
||||
const value: terminal_hyperlink.Hyperlink = .{
|
||||
.id = .{ .implicit = 0x01020304 },
|
||||
@@ -280,10 +289,18 @@ test "golden implicit encoding" {
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
try encode(value, &writer);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
"\x01\x04\x03\x02\x01\x03\x00\x00\x00uri",
|
||||
try test_fixture.expectEqual(
|
||||
.bytes,
|
||||
"src/terminal/snapshot/testdata/hyperlink-implicit-v1.hex",
|
||||
"snapshot_fixture-hyperlink-implicit-v1.hex",
|
||||
&test_implicit_fixture,
|
||||
writer.buffered(),
|
||||
);
|
||||
|
||||
var reader: std.Io.Reader = .fixed(&test_implicit_fixture);
|
||||
var decoded = try decode(&reader, std.testing.allocator);
|
||||
defer decoded.deinit(std.testing.allocator);
|
||||
try std.testing.expectEqualDeep(value, decoded);
|
||||
}
|
||||
|
||||
test "golden explicit encoding" {
|
||||
@@ -296,10 +313,18 @@ test "golden explicit encoding" {
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
try encode(value, &writer);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
"\x02\x02\x00\x00\x00id\x03\x00\x00\x00uri",
|
||||
try test_fixture.expectEqual(
|
||||
.bytes,
|
||||
"src/terminal/snapshot/testdata/hyperlink-explicit-v1.hex",
|
||||
"snapshot_fixture-hyperlink-explicit-v1.hex",
|
||||
&test_explicit_fixture,
|
||||
writer.buffered(),
|
||||
);
|
||||
|
||||
var reader: std.Io.Reader = .fixed(&test_explicit_fixture);
|
||||
var decoded = try decode(&reader, std.testing.allocator);
|
||||
defer decoded.deinit(std.testing.allocator);
|
||||
try std.testing.expectEqualDeep(value, decoded);
|
||||
}
|
||||
|
||||
test "decode rejects empty strings" {
|
||||
@@ -369,9 +394,7 @@ test "reject invalid kinds" {
|
||||
|
||||
test "decode allocation failure" {
|
||||
{
|
||||
var reader: std.Io.Reader = .fixed(
|
||||
"\x01\x04\x03\x02\x01\x03\x00\x00\x00uri",
|
||||
);
|
||||
var reader: std.Io.Reader = .fixed(&test_implicit_fixture);
|
||||
var failing = std.testing.FailingAllocator.init(
|
||||
std.testing.allocator,
|
||||
.{ .fail_index = 0 },
|
||||
@@ -387,9 +410,7 @@ test "decode allocation failure" {
|
||||
std.testing.allocator,
|
||||
.{ .fail_index = fail_index },
|
||||
);
|
||||
var reader: std.Io.Reader = .fixed(
|
||||
"\x02\x02\x00\x00\x00id\x03\x00\x00\x00uri",
|
||||
);
|
||||
var reader: std.Io.Reader = .fixed(&test_explicit_fixture);
|
||||
try std.testing.expectError(
|
||||
error.OutOfMemory,
|
||||
decode(&reader, failing.allocator()),
|
||||
@@ -399,8 +420,8 @@ test "decode allocation failure" {
|
||||
|
||||
test "reject every truncation" {
|
||||
const fixtures: [2][]const u8 = .{
|
||||
"\x01\x04\x03\x02\x01\x03\x00\x00\x00uri",
|
||||
"\x02\x02\x00\x00\x00id\x03\x00\x00\x00uri",
|
||||
&test_implicit_fixture,
|
||||
&test_explicit_fixture,
|
||||
};
|
||||
|
||||
for (fixtures) |fixture| {
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const test_fixture = @import("fixture.zig");
|
||||
const grid = @import("grid.zig");
|
||||
const hyperlink = @import("hyperlink.zig");
|
||||
const io = @import("io.zig");
|
||||
@@ -546,35 +547,17 @@ fn pageHyperlink(
|
||||
};
|
||||
}
|
||||
|
||||
// Regenerate these after a wire-format change from `writer.buffered()` in the
|
||||
// sparse-page test and `destination.written()` in the empty-record test below.
|
||||
// Format those byte slices as Zig-escaped strings before replacing the literals.
|
||||
const test_page_fixture =
|
||||
"\x03\x00\x02\x00\x02\x00\x02\x00\x08\x00\x00\x02\x80\x00\x00\x00" ++
|
||||
"\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ++
|
||||
"\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x01\x2a\x00\x00" ++
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x01\x00\x00\x00\x61" ++
|
||||
"\x05\x00\x00\x00\x61\x6c\x70\x68\x61\x03\x00\x01\x04\x03\x02\x01" ++
|
||||
"\x04\x00\x00\x00\x62\x65\x74\x61\x04\x00\x01\x05\x00\x01\x00\x01" ++
|
||||
"\x00\x41\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x00\x03\x00\x03" ++
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x01" ++
|
||||
"\x00\x07\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00" ++
|
||||
"\x00\x00\x78\x00\x00\x00\x02\x00\x00\x00\x01\x03\x00\x00\x02\x03" ++
|
||||
"\x00\x00\x02\x00\x01\x00\x00\x00\x00\x00\xaa\xbb\xcc\x00\x00\x00" ++
|
||||
"\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ++
|
||||
"\x00\x00";
|
||||
const test_page_fixture = test_fixture.parse(
|
||||
@embedFile("testdata/page-v1.hex"),
|
||||
);
|
||||
|
||||
const test_empty_framed_page_fixture =
|
||||
// Record header: PAGE, 37-byte payload, CRC32C.
|
||||
"\x03\x00\x25\x00\x00\x00\x8c\x05\xd6\xd3" ++
|
||||
// PAGE header: one column, one row, and zero counts/capacities.
|
||||
"\x01\x00\x01\x00\x00\x00\x00\x00" ++
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00" ++
|
||||
"\x00\x00\x00\x00" ++
|
||||
// One default row and one empty cell.
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00" ++
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00" ++
|
||||
"\x00";
|
||||
const test_header_fixture = test_fixture.parse(
|
||||
@embedFile("testdata/page-header-v1.hex"),
|
||||
);
|
||||
|
||||
const test_empty_framed_page_fixture = test_fixture.parse(
|
||||
@embedFile("testdata/page-empty-record-v1.hex"),
|
||||
);
|
||||
|
||||
test "PAGE header golden encoding and decoding" {
|
||||
const header: Header = .{
|
||||
@@ -587,29 +570,26 @@ test "PAGE header golden encoding and decoding" {
|
||||
.grapheme_capacity_bytes = 0x0d0e0f10,
|
||||
.string_capacity_bytes = 0x11121314,
|
||||
};
|
||||
const fixture =
|
||||
"\x02\x01\x04\x03\x06\x05\x08\x07" ++
|
||||
"\x0a\x09\x0c\x0b\x10\x0f\x0e\x0d" ++
|
||||
"\x14\x13\x12\x11";
|
||||
|
||||
var buf: [Header.len]u8 = undefined;
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
try header.encode(&writer);
|
||||
try std.testing.expectEqualStrings(fixture, writer.buffered());
|
||||
try test_fixture.expectEqual(
|
||||
.bytes,
|
||||
"src/terminal/snapshot/testdata/page-header-v1.hex",
|
||||
"snapshot_fixture-page-header-v1.hex",
|
||||
&test_header_fixture,
|
||||
writer.buffered(),
|
||||
);
|
||||
|
||||
var source: std.Io.Reader = .fixed(fixture);
|
||||
var source: std.Io.Reader = .fixed(&test_header_fixture);
|
||||
var read_buf: [1]u8 = undefined;
|
||||
var limited = source.limited(.unlimited, &read_buf);
|
||||
try std.testing.expectEqual(header, try Header.decode(&limited.interface));
|
||||
}
|
||||
|
||||
test "reject every truncation" {
|
||||
const fixture =
|
||||
"\x02\x01\x04\x03\x06\x05\x08\x07" ++
|
||||
"\x0a\x09\x0c\x0b\x10\x0f\x0e\x0d" ++
|
||||
"\x14\x13\x12\x11";
|
||||
for (0..Header.len) |len| {
|
||||
var reader: std.Io.Reader = .fixed(fixture[0..len]);
|
||||
var reader: std.Io.Reader = .fixed(test_header_fixture[0..len]);
|
||||
try std.testing.expectError(error.EndOfStream, Header.decode(&reader));
|
||||
}
|
||||
}
|
||||
@@ -729,12 +709,21 @@ test "framed PAGE encode and decode a sparse native page" {
|
||||
var writer: std.Io.Writer = .fixed(&encoded);
|
||||
try encodePayload(&page, &writer);
|
||||
|
||||
const fixture = test_page_fixture;
|
||||
try std.testing.expectEqualStrings(fixture, writer.buffered());
|
||||
try std.testing.expectEqual(@as(u64, fixture.len), counter.count);
|
||||
try test_fixture.expectEqual(
|
||||
.page,
|
||||
"src/terminal/snapshot/testdata/page-v1.hex",
|
||||
"snapshot_fixture-page-v1.hex",
|
||||
&test_page_fixture,
|
||||
writer.buffered(),
|
||||
);
|
||||
try std.testing.expectEqual(
|
||||
@as(u64, test_page_fixture.len),
|
||||
counter.count,
|
||||
);
|
||||
|
||||
// Decode the checked-in reference rather than the just-generated bytes.
|
||||
// A one-byte backing buffer exercises streaming reads across every field.
|
||||
var source: std.Io.Reader = .fixed(writer.buffered());
|
||||
var source: std.Io.Reader = .fixed(&test_page_fixture);
|
||||
var read_buf: [1]u8 = undefined;
|
||||
var limited = source.limited(.unlimited, &read_buf);
|
||||
var decoded = try decodePayload(
|
||||
@@ -865,12 +854,15 @@ test "framed PAGE golden empty record" {
|
||||
var destination: std.Io.Writer.Allocating = .init(std.testing.allocator);
|
||||
defer destination.deinit();
|
||||
try encode(&page, &destination);
|
||||
try std.testing.expectEqualStrings(
|
||||
test_empty_framed_page_fixture,
|
||||
try test_fixture.expectEqual(
|
||||
.bytes,
|
||||
"src/terminal/snapshot/testdata/page-empty-record-v1.hex",
|
||||
"snapshot_fixture-page-empty-record-v1.hex",
|
||||
&test_empty_framed_page_fixture,
|
||||
destination.written(),
|
||||
);
|
||||
|
||||
var source: std.Io.Reader = .fixed(destination.written());
|
||||
var source: std.Io.Reader = .fixed(&test_empty_framed_page_fixture);
|
||||
var decoded = try decode(&source, std.testing.allocator);
|
||||
defer decoded.deinit();
|
||||
try std.testing.expectEqual(
|
||||
@@ -880,7 +872,7 @@ test "framed PAGE golden empty record" {
|
||||
}
|
||||
|
||||
test "framed PAGE rejects a different record tag" {
|
||||
var wrong_tag = test_empty_framed_page_fixture.*;
|
||||
var wrong_tag = test_empty_framed_page_fixture;
|
||||
std.mem.writeInt(
|
||||
u16,
|
||||
wrong_tag[0..2],
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
//! Supported tags are in `Tag`.
|
||||
|
||||
const std = @import("std");
|
||||
const test_fixture = @import("fixture.zig");
|
||||
const io = @import("io.zig");
|
||||
|
||||
/// CRC32C as specified by the snapshot format. Zig names this standard
|
||||
@@ -295,6 +296,10 @@ fn encodeChecksumPrefix(
|
||||
try io.writeInt(writer, u32, payload_len);
|
||||
}
|
||||
|
||||
const test_page_header_fixture = test_fixture.parse(
|
||||
@embedFile("testdata/record-page-header-v1.hex"),
|
||||
);
|
||||
|
||||
test "golden PAGE record header and checksum" {
|
||||
const page_header =
|
||||
"\x50\x00\x18\x00\x00\x00\x00\x00" ++
|
||||
@@ -314,10 +319,16 @@ test "golden PAGE record header and checksum" {
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
try header.encode(&writer);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
"\x03\x00\x18\x00\x00\x00\x1b\x44\x78\x71",
|
||||
try test_fixture.expectEqual(
|
||||
.bytes,
|
||||
"src/terminal/snapshot/testdata/record-page-header-v1.hex",
|
||||
"snapshot_fixture-record-page-header-v1.hex",
|
||||
&test_page_header_fixture,
|
||||
writer.buffered(),
|
||||
);
|
||||
|
||||
var reader: std.Io.Reader = .fixed(&test_page_header_fixture);
|
||||
try std.testing.expectEqual(header, try Header.decode(&reader));
|
||||
}
|
||||
|
||||
test "reject invalid tags" {
|
||||
@@ -330,9 +341,10 @@ test "reject invalid tags" {
|
||||
}
|
||||
|
||||
test "reject every header truncation" {
|
||||
const fixture = "\x03\x00\x18\x00\x00\x00\x1b\x44\x78\x71";
|
||||
for (0..Header.len) |len| {
|
||||
var reader: std.Io.Reader = .fixed(fixture[0..len]);
|
||||
var reader: std.Io.Reader = .fixed(
|
||||
test_page_header_fixture[0..len],
|
||||
);
|
||||
try std.testing.expectError(error.EndOfStream, Header.decode(&reader));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +201,7 @@ const std = @import("std");
|
||||
const build_options = @import("terminal_options");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const hyperlink = @import("hyperlink.zig");
|
||||
const test_fixture = @import("fixture.zig");
|
||||
const io = @import("io.zig");
|
||||
const page = @import("page.zig");
|
||||
const record = @import("record.zig");
|
||||
@@ -1116,19 +1117,13 @@ pub fn decodeCursorHyperlink(
|
||||
return try hyperlink.decode(reader, alloc);
|
||||
}
|
||||
|
||||
const test_header_fixture =
|
||||
"\x01\x00\x03\x02\x05\x04\x07\x06\x03\x19" ++
|
||||
"\x00\x00\x00\x00\x01\x7f\x00\x00" ++
|
||||
"\x02\x12\x34\x56\xff\x03\x00\x00" ++
|
||||
"\x0d\x0c\x0b\x0a\xe4\x3d\x02\x07" ++
|
||||
"\x01\x02\x04\x08\x10\x1f\x00\x11" ++
|
||||
"\x02\x03\x01";
|
||||
const test_header_fixture = test_fixture.parse(
|
||||
@embedFile("testdata/screen-header-v1.hex"),
|
||||
);
|
||||
|
||||
const test_saved_cursor_fixture =
|
||||
"\x02\x01\x04\x03" ++
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00" ++
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00" ++
|
||||
"\x07\xe4\x3d";
|
||||
const test_saved_cursor_fixture = test_fixture.parse(
|
||||
@embedFile("testdata/screen-saved-cursor-v1.hex"),
|
||||
);
|
||||
|
||||
fn testCharsetState() TerminalScreen.CharsetState {
|
||||
var result: TerminalScreen.CharsetState = .{
|
||||
@@ -1220,18 +1215,20 @@ fn testSavedCursor() SavedCursor {
|
||||
}
|
||||
|
||||
test "SCREEN header golden encoding and decoding" {
|
||||
try std.testing.expectEqual(Header.len, test_header_fixture.len);
|
||||
|
||||
var encoded: [Header.len]u8 = undefined;
|
||||
var writer: std.Io.Writer = .fixed(&encoded);
|
||||
try testHeader().encode(&writer);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
test_header_fixture,
|
||||
try test_fixture.expectEqual(
|
||||
.bytes,
|
||||
"src/terminal/snapshot/testdata/screen-header-v1.hex",
|
||||
"snapshot_fixture-screen-header-v1.hex",
|
||||
&test_header_fixture,
|
||||
writer.buffered(),
|
||||
);
|
||||
try std.testing.expectEqual(Header.len, test_header_fixture.len);
|
||||
|
||||
var source: std.Io.Reader = .fixed(test_header_fixture);
|
||||
var source: std.Io.Reader = .fixed(&test_header_fixture);
|
||||
var buffer: [1]u8 = undefined;
|
||||
var limited = source.limited(.unlimited, &buffer);
|
||||
|
||||
@@ -1601,20 +1598,22 @@ test "native SCREEN payload omits absent optional state" {
|
||||
}
|
||||
|
||||
test "saved cursor golden encoding and decoding" {
|
||||
var encoded: [SavedCursor.len]u8 = undefined;
|
||||
var writer: std.Io.Writer = .fixed(&encoded);
|
||||
try testSavedCursor().encode(&writer);
|
||||
try test_fixture.expectEqual(
|
||||
.bytes,
|
||||
"src/terminal/snapshot/testdata/screen-saved-cursor-v1.hex",
|
||||
"snapshot_fixture-screen-saved-cursor-v1.hex",
|
||||
&test_saved_cursor_fixture,
|
||||
writer.buffered(),
|
||||
);
|
||||
try std.testing.expectEqual(
|
||||
SavedCursor.len,
|
||||
test_saved_cursor_fixture.len,
|
||||
);
|
||||
|
||||
var encoded: [SavedCursor.len]u8 = undefined;
|
||||
var writer: std.Io.Writer = .fixed(&encoded);
|
||||
try testSavedCursor().encode(&writer);
|
||||
try std.testing.expectEqualStrings(
|
||||
test_saved_cursor_fixture,
|
||||
writer.buffered(),
|
||||
);
|
||||
|
||||
var source: std.Io.Reader = .fixed(test_saved_cursor_fixture);
|
||||
var source: std.Io.Reader = .fixed(&test_saved_cursor_fixture);
|
||||
var buffer: [1]u8 = undefined;
|
||||
var limited = source.limited(.unlimited, &buffer);
|
||||
try std.testing.expectEqualDeep(
|
||||
|
||||
@@ -5,14 +5,20 @@ const build_options = @import("terminal_options");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const checkpoint = @import("checkpoint.zig");
|
||||
const envelope = @import("envelope.zig");
|
||||
const test_fixture = @import("fixture.zig");
|
||||
const history = @import("history.zig");
|
||||
const record = @import("record.zig");
|
||||
const screen = @import("screen.zig");
|
||||
const terminal = @import("terminal.zig");
|
||||
const Terminal = @import("../Terminal.zig");
|
||||
const TerminalPageList = @import("../PageList.zig");
|
||||
const TerminalScreen = @import("../Screen.zig");
|
||||
const TerminalScreenKey = @import("../ScreenSet.zig").Key;
|
||||
|
||||
const test_complete_fixture = test_fixture.parse(
|
||||
@embedFile("testdata/complete-v1.hex"),
|
||||
);
|
||||
|
||||
/// Errors possible while encoding one complete terminal snapshot.
|
||||
pub const EncodeError = terminal.EncodeError ||
|
||||
screen.EncodeError ||
|
||||
@@ -196,15 +202,14 @@ test "complete snapshot round trip with history and alternate screen" {
|
||||
const testing = std.testing;
|
||||
|
||||
var t = try Terminal.init(testing.io, testing.allocator, .{
|
||||
.cols = 80,
|
||||
.cols = 2,
|
||||
.rows = 3,
|
||||
.max_scrollback_bytes = null,
|
||||
.max_scrollback_lines = null,
|
||||
});
|
||||
defer t.deinit(testing.allocator);
|
||||
|
||||
// Exercise terminal-wide state and grow the primary screen until its
|
||||
// active area is preceded by multiple complete history pages.
|
||||
// Exercise terminal-wide state.
|
||||
t.width_px = 800;
|
||||
t.height_px = 600;
|
||||
t.colors.palette.set(7, .{ .r = 1, .g = 2, .b = 3 });
|
||||
@@ -213,9 +218,58 @@ test "complete snapshot round trip with history and alternate screen" {
|
||||
try t.setTitle("complete snapshot");
|
||||
|
||||
const primary = t.screens.get(.primary).?;
|
||||
while (primary.pages.totalPages() < 4) {
|
||||
try t.printString("primary history\n");
|
||||
}
|
||||
|
||||
// Use small exact capacities so this compound golden remains practical to
|
||||
// review while still containing two complete history pages and one active
|
||||
// page. Replacing the Screen in place preserves ScreenSet routing.
|
||||
var replacement: TerminalScreen = replacement: {
|
||||
var builder = try TerminalPageList.Builder.init(
|
||||
testing.allocator,
|
||||
.{
|
||||
.cols = t.cols,
|
||||
.rows = t.rows,
|
||||
.max_size = null,
|
||||
.max_lines = null,
|
||||
},
|
||||
);
|
||||
defer builder.deinit();
|
||||
|
||||
const oldest = try builder.allocatePage(.{ .cols = 2, .rows = 2 });
|
||||
oldest.size.rows = 2;
|
||||
oldest.getRowAndCell(0, 0).cell.* = .init('A');
|
||||
|
||||
const recent = try builder.allocatePage(.{ .cols = 2, .rows = 2 });
|
||||
recent.size.rows = 2;
|
||||
recent.getRowAndCell(0, 0).cell.* = .init('B');
|
||||
|
||||
const active = try builder.allocatePage(.{ .cols = 2, .rows = 3 });
|
||||
active.size.rows = 3;
|
||||
active.getRowAndCell(0, 0).cell.* = .init('C');
|
||||
active.getRowAndCell(0, 1).cell.* = .init('D');
|
||||
active.getRowAndCell(0, 2).cell.* = .init('E');
|
||||
|
||||
var pages = try builder.finish();
|
||||
errdefer pages.deinit();
|
||||
|
||||
const cursor_pin = try pages.trackPin(
|
||||
pages.pin(.{ .active = .{} }).?,
|
||||
);
|
||||
const cursor_rac = cursor_pin.rowAndCell();
|
||||
break :replacement .{
|
||||
.io = testing.io,
|
||||
.alloc = testing.allocator,
|
||||
.pages = pages,
|
||||
.cursor = .{
|
||||
.page_pin = cursor_pin,
|
||||
.page_row = cursor_rac.row,
|
||||
.page_cell = cursor_rac.cell,
|
||||
},
|
||||
};
|
||||
};
|
||||
primary.deinit();
|
||||
primary.* = replacement;
|
||||
replacement = undefined;
|
||||
|
||||
try testing.expect(primary.pages.scrollbar().total > t.rows);
|
||||
|
||||
// Compression is an internal source representation and must remain
|
||||
@@ -232,8 +286,16 @@ test "complete snapshot round trip with history and alternate screen" {
|
||||
defer encoded.deinit();
|
||||
try encode(&t, &encoded);
|
||||
try testing.expectEqualDeep(source_memory, primary.pages.memoryStats());
|
||||
try test_fixture.expectEqual(
|
||||
.snapshot,
|
||||
"src/terminal/snapshot/testdata/complete-v1.hex",
|
||||
"snapshot_fixture-complete-v1.hex",
|
||||
&test_complete_fixture,
|
||||
encoded.written(),
|
||||
);
|
||||
|
||||
var encoded_source: std.Io.Reader = .fixed(encoded.written());
|
||||
// Restore the checked-in reference rather than the just-generated bytes.
|
||||
var encoded_source: std.Io.Reader = .fixed(&test_complete_fixture);
|
||||
var source_buffer: [1]u8 = undefined;
|
||||
var limited = encoded_source.limited(.unlimited, &source_buffer);
|
||||
var restored = try decode(
|
||||
@@ -266,7 +328,10 @@ test "complete snapshot round trip with history and alternate screen" {
|
||||
var reencoded: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer reencoded.deinit();
|
||||
try encode(&restored, &reencoded);
|
||||
try testing.expectEqualStrings(encoded.written(), reencoded.written());
|
||||
try testing.expectEqualStrings(
|
||||
&test_complete_fixture,
|
||||
reencoded.written(),
|
||||
);
|
||||
|
||||
// SCREEN keys make their order independent. Keep HISTORY canonical here;
|
||||
// only the active-state screen sequences are intentionally reversed.
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
//! Underline values 6 and 7 are invalid in snapshot version 1.
|
||||
|
||||
const std = @import("std");
|
||||
const test_fixture = @import("fixture.zig");
|
||||
const io = @import("io.zig");
|
||||
const sgr = @import("../sgr.zig");
|
||||
const terminal_style = @import("../style.zig");
|
||||
@@ -215,6 +216,10 @@ fn computeLen() usize {
|
||||
}
|
||||
}
|
||||
|
||||
const test_golden_fixture = test_fixture.parse(
|
||||
@embedFile("testdata/style-v1.hex"),
|
||||
);
|
||||
|
||||
test "golden encoding and decoding" {
|
||||
const value: terminal_style.Style = .{
|
||||
.fg_color = .none,
|
||||
@@ -236,18 +241,18 @@ test "golden encoding and decoding" {
|
||||
.underline = .curly,
|
||||
},
|
||||
};
|
||||
const fixture =
|
||||
"\x00\x00\x00\x00" ++
|
||||
"\x01\x7f\x00\x00" ++
|
||||
"\x02\x12\x34\x56" ++
|
||||
"\xff\x03\x00\x00";
|
||||
|
||||
var buf: [len]u8 = undefined;
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
try encode(value, &writer);
|
||||
try std.testing.expectEqualStrings(fixture, writer.buffered());
|
||||
try test_fixture.expectEqual(
|
||||
.bytes,
|
||||
"src/terminal/snapshot/testdata/style-v1.hex",
|
||||
"snapshot_fixture-style-v1.hex",
|
||||
&test_golden_fixture,
|
||||
writer.buffered(),
|
||||
);
|
||||
|
||||
var source: std.Io.Reader = .fixed(fixture);
|
||||
var source: std.Io.Reader = .fixed(&test_golden_fixture);
|
||||
var read_buf: [1]u8 = undefined;
|
||||
var limited = source.limited(.unlimited, &read_buf);
|
||||
try std.testing.expect(value.eql(try decode(&limited.interface)));
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const test_fixture = @import("fixture.zig");
|
||||
const io = @import("io.zig");
|
||||
const record = @import("record.zig");
|
||||
const terminal_ansi = @import("../ansi.zig");
|
||||
@@ -1180,20 +1181,9 @@ const test_header: Header = header: {
|
||||
};
|
||||
};
|
||||
|
||||
const test_header_fixture =
|
||||
"\x02\x01\x04\x03\x08\x07\x06\x05\x0c\x0b\x0a\x09" ++
|
||||
"\x01\x00\x02\x00\x03\x00\x04\x00" ++
|
||||
"\x01\x01\x00\x02\x00\x41\x00\x00\x00" ++
|
||||
"\x01\x03\x02" ++
|
||||
"\x02\x01\x04\x04\x01\x21\x01" ++
|
||||
"\x01\x00\x00\x00\x00\x00\x00\x00" ++
|
||||
"\x00\x00\x00\x00\x00\x02\x00\x00" ++
|
||||
"\x01\x00\x00\x00\x00\x02\x00\x00" ++
|
||||
"\x01\x01\x02\x03\x00\x00\x00\x00" ++
|
||||
"\x00\x00\x00\x00\x01\x04\x05\x06" ++
|
||||
"\x01\x07\x08\x09\x01\x0a\x0b\x0c" ++
|
||||
"\xff\xff\xff\xff\xff\xff\xff\xff" ++
|
||||
"\x08\x07\x06\x05\x04\x03\x02\x01";
|
||||
const test_header_fixture = test_fixture.parse(
|
||||
@embedFile("testdata/terminal-header-v1.hex"),
|
||||
);
|
||||
|
||||
test "TERMINAL mode bit layout" {
|
||||
try std.testing.expectEqual(
|
||||
@@ -1231,15 +1221,21 @@ test "TERMINAL mode bit layout" {
|
||||
|
||||
test "TERMINAL header golden encoding and decoding" {
|
||||
const testing = std.testing;
|
||||
try testing.expectEqual(Header.len, test_header_fixture.len);
|
||||
|
||||
var encoded: [Header.len]u8 = undefined;
|
||||
var writer: std.Io.Writer = .fixed(&encoded);
|
||||
try test_header.encode(&writer);
|
||||
try testing.expectEqualStrings(test_header_fixture, writer.buffered());
|
||||
try test_fixture.expectEqual(
|
||||
.bytes,
|
||||
"src/terminal/snapshot/testdata/terminal-header-v1.hex",
|
||||
"snapshot_fixture-terminal-header-v1.hex",
|
||||
&test_header_fixture,
|
||||
writer.buffered(),
|
||||
);
|
||||
try testing.expectEqual(Header.len, test_header_fixture.len);
|
||||
|
||||
// Exercise the streaming path with less buffered data than every integer.
|
||||
var source: std.Io.Reader = .fixed(test_header_fixture);
|
||||
var source: std.Io.Reader = .fixed(&test_header_fixture);
|
||||
var buffer: [1]u8 = undefined;
|
||||
var limited = source.limited(.unlimited, &buffer);
|
||||
try testing.expectEqualDeep(
|
||||
@@ -1306,14 +1302,14 @@ test "TERMINAL header rejects invalid values" {
|
||||
.{ .offset = 68, .value = 1, .expected = error.InvalidDynamicRGB },
|
||||
};
|
||||
for (byte_cases) |case| {
|
||||
var fixture = test_header_fixture.*;
|
||||
var fixture = test_header_fixture;
|
||||
fixture[case.offset] = case.value;
|
||||
var reader: std.Io.Reader = .fixed(&fixture);
|
||||
try testing.expectError(case.expected, Header.decode(&reader));
|
||||
}
|
||||
|
||||
// Cross-field and multi-byte invariants are validated after decoding.
|
||||
var invalid_screen_count = test_header_fixture.*;
|
||||
var invalid_screen_count = test_header_fixture;
|
||||
invalid_screen_count[23] = 3;
|
||||
var screen_count_reader: std.Io.Reader = .fixed(&invalid_screen_count);
|
||||
try testing.expectError(
|
||||
@@ -1321,7 +1317,7 @@ test "TERMINAL header rejects invalid values" {
|
||||
Header.decode(&screen_count_reader),
|
||||
);
|
||||
|
||||
var missing_active_screen = test_header_fixture.*;
|
||||
var missing_active_screen = test_header_fixture;
|
||||
missing_active_screen[23] = 1;
|
||||
var active_screen_reader: std.Io.Reader = .fixed(&missing_active_screen);
|
||||
try testing.expectError(
|
||||
@@ -1329,7 +1325,7 @@ test "TERMINAL header rejects invalid values" {
|
||||
Header.decode(&active_screen_reader),
|
||||
);
|
||||
|
||||
var invalid_scrolling_region = test_header_fixture.*;
|
||||
var invalid_scrolling_region = test_header_fixture;
|
||||
invalid_scrolling_region[14] = 0x04;
|
||||
invalid_scrolling_region[15] = 0x03;
|
||||
var scrolling_region_reader: std.Io.Reader = .fixed(
|
||||
@@ -1340,7 +1336,7 @@ test "TERMINAL header rejects invalid values" {
|
||||
Header.decode(&scrolling_region_reader),
|
||||
);
|
||||
|
||||
var invalid_codepoint = test_header_fixture.*;
|
||||
var invalid_codepoint = test_header_fixture;
|
||||
invalid_codepoint[25] = 0x00;
|
||||
invalid_codepoint[26] = 0xd8;
|
||||
invalid_codepoint[27] = 0x00;
|
||||
|
||||
138
src/terminal/snapshot/testdata/complete-v1.hex
vendored
Normal file
138
src/terminal/snapshot/testdata/complete-v1.hex
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
# Ghostty snapshot fixture
|
||||
# Wire version: 1
|
||||
# Generated by its snapshot test; review before replacing.
|
||||
# On mismatch, the candidate is copied to the repository root.
|
||||
|
||||
# offset 0x00000000: snapshot envelope
|
||||
47 48 4f 53 54 53 4e 50 01 00 # 0x00000000
|
||||
|
||||
# offset 0x0000000a: terminal record, payload 952 bytes
|
||||
01 00 b8 03 00 00 e3 5f 8b cc 02 00 03 00 20 03 # 0x0000000a
|
||||
00 00 58 02 00 00 00 00 02 00 00 00 01 00 00 01 # 0x0000001a
|
||||
00 02 00 65 00 00 00 01 01 01 00 00 00 00 00 08 # 0x0000002a
|
||||
00 04 22 00 64 10 00 00 00 04 22 00 64 00 00 00 # 0x0000003a
|
||||
00 04 22 00 64 00 00 00 00 00 00 00 00 00 00 00 # 0x0000004a
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x0000005a
|
||||
00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff # 0x0000006a
|
||||
ff 00 1d 1f 21 cc 66 66 b5 bd 68 f0 c6 74 81 a2 # 0x0000007a
|
||||
be b2 94 bb 8a be b7 c5 c8 c6 66 66 66 d5 4e 53 # 0x0000008a
|
||||
b9 ca 4a e7 c5 47 7a a6 da c3 97 d8 70 c0 b1 ea # 0x0000009a
|
||||
ea ea 00 00 00 00 00 5f 00 00 87 00 00 af 00 00 # 0x000000aa
|
||||
d7 00 00 ff 00 5f 00 00 5f 5f 00 5f 87 00 5f af # 0x000000ba
|
||||
00 5f d7 00 5f ff 00 87 00 00 87 5f 00 87 87 00 # 0x000000ca
|
||||
87 af 00 87 d7 00 87 ff 00 af 00 00 af 5f 00 af # 0x000000da
|
||||
87 00 af af 00 af d7 00 af ff 00 d7 00 00 d7 5f # 0x000000ea
|
||||
00 d7 87 00 d7 af 00 d7 d7 00 d7 ff 00 ff 00 00 # 0x000000fa
|
||||
ff 5f 00 ff 87 00 ff af 00 ff d7 00 ff ff 5f 00 # 0x0000010a
|
||||
00 5f 00 5f 5f 00 87 5f 00 af 5f 00 d7 5f 00 ff # 0x0000011a
|
||||
5f 5f 00 5f 5f 5f 5f 5f 87 5f 5f af 5f 5f d7 5f # 0x0000012a
|
||||
5f ff 5f 87 00 5f 87 5f 5f 87 87 5f 87 af 5f 87 # 0x0000013a
|
||||
d7 5f 87 ff 5f af 00 5f af 5f 5f af 87 5f af af # 0x0000014a
|
||||
5f af d7 5f af ff 5f d7 00 5f d7 5f 5f d7 87 5f # 0x0000015a
|
||||
d7 af 5f d7 d7 5f d7 ff 5f ff 00 5f ff 5f 5f ff # 0x0000016a
|
||||
87 5f ff af 5f ff d7 5f ff ff 87 00 00 87 00 5f # 0x0000017a
|
||||
87 00 87 87 00 af 87 00 d7 87 00 ff 87 5f 00 87 # 0x0000018a
|
||||
5f 5f 87 5f 87 87 5f af 87 5f d7 87 5f ff 87 87 # 0x0000019a
|
||||
00 87 87 5f 87 87 87 87 87 af 87 87 d7 87 87 ff # 0x000001aa
|
||||
87 af 00 87 af 5f 87 af 87 87 af af 87 af d7 87 # 0x000001ba
|
||||
af ff 87 d7 00 87 d7 5f 87 d7 87 87 d7 af 87 d7 # 0x000001ca
|
||||
d7 87 d7 ff 87 ff 00 87 ff 5f 87 ff 87 87 ff af # 0x000001da
|
||||
87 ff d7 87 ff ff af 00 00 af 00 5f af 00 87 af # 0x000001ea
|
||||
00 af af 00 d7 af 00 ff af 5f 00 af 5f 5f af 5f # 0x000001fa
|
||||
87 af 5f af af 5f d7 af 5f ff af 87 00 af 87 5f # 0x0000020a
|
||||
af 87 87 af 87 af af 87 d7 af 87 ff af af 00 af # 0x0000021a
|
||||
af 5f af af 87 af af af af af d7 af af ff af d7 # 0x0000022a
|
||||
00 af d7 5f af d7 87 af d7 af af d7 d7 af d7 ff # 0x0000023a
|
||||
af ff 00 af ff 5f af ff 87 af ff af af ff d7 af # 0x0000024a
|
||||
ff ff d7 00 00 d7 00 5f d7 00 87 d7 00 af d7 00 # 0x0000025a
|
||||
d7 d7 00 ff d7 5f 00 d7 5f 5f d7 5f 87 d7 5f af # 0x0000026a
|
||||
d7 5f d7 d7 5f ff d7 87 00 d7 87 5f d7 87 87 d7 # 0x0000027a
|
||||
87 af d7 87 d7 d7 87 ff d7 af 00 d7 af 5f d7 af # 0x0000028a
|
||||
87 d7 af af d7 af d7 d7 af ff d7 d7 00 d7 d7 5f # 0x0000029a
|
||||
d7 d7 87 d7 d7 af d7 d7 d7 d7 d7 ff d7 ff 00 d7 # 0x000002aa
|
||||
ff 5f d7 ff 87 d7 ff af d7 ff d7 d7 ff ff ff 00 # 0x000002ba
|
||||
00 ff 00 5f ff 00 87 ff 00 af ff 00 d7 ff 00 ff # 0x000002ca
|
||||
ff 5f 00 ff 5f 5f ff 5f 87 ff 5f af ff 5f d7 ff # 0x000002da
|
||||
5f ff ff 87 00 ff 87 5f ff 87 87 ff 87 af ff 87 # 0x000002ea
|
||||
d7 ff 87 ff ff af 00 ff af 5f ff af 87 ff af af # 0x000002fa
|
||||
ff af d7 ff af ff ff d7 00 ff d7 5f ff d7 87 ff # 0x0000030a
|
||||
d7 af ff d7 d7 ff d7 ff ff ff 00 ff ff 5f ff ff # 0x0000031a
|
||||
87 ff ff af ff ff d7 ff ff ff 08 08 08 12 12 12 # 0x0000032a
|
||||
1c 1c 1c 26 26 26 30 30 30 3a 3a 3a 44 44 44 4e # 0x0000033a
|
||||
4e 4e 58 58 58 62 62 62 6c 6c 6c 76 76 76 80 80 # 0x0000034a
|
||||
80 8a 8a 8a 94 94 94 9e 9e 9e a8 a8 a8 b2 b2 b2 # 0x0000035a
|
||||
bc bc bc c6 c6 c6 d0 d0 d0 da da da e4 e4 e4 ee # 0x0000036a
|
||||
ee ee 80 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x0000037a
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x0000038a
|
||||
00 00 01 02 03 14 00 00 00 66 69 6c 65 3a 2f 2f # 0x0000039a
|
||||
2f 74 6d 70 2f 73 6e 61 70 73 68 6f 74 11 00 00 # 0x000003aa
|
||||
00 63 6f 6d 70 6c 65 74 65 20 73 6e 61 70 73 68 # 0x000003ba
|
||||
6f 74 # 0x000003ca
|
||||
|
||||
# offset 0x000003cc: screen record, payload 46 bytes
|
||||
02 00 2e 00 00 00 dc 02 f1 37 00 00 01 00 00 00 # 0x000003cc
|
||||
00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x000003dc
|
||||
00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 # 0x000003ec
|
||||
00 00 00 00 00 00 00 00 # 0x000003fc
|
||||
|
||||
# offset 0x00000404: page record, payload 119 bytes
|
||||
03 00 77 00 00 00 48 b7 91 cb 02 00 03 00 00 00 # 0x00000404
|
||||
00 00 10 00 c0 00 00 04 00 00 00 08 00 00 00 00 # 0x00000414
|
||||
00 00 00 00 00 00 00 43 00 00 00 00 00 00 00 00 # 0x00000424
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000434
|
||||
00 00 00 00 00 00 00 00 44 00 00 00 00 00 00 00 # 0x00000444
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000454
|
||||
00 00 00 00 00 00 00 00 00 45 00 00 00 00 00 00 # 0x00000464
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000474
|
||||
00 # 0x00000484
|
||||
|
||||
# offset 0x00000485: screen record, payload 46 bytes
|
||||
02 00 2e 00 00 00 05 b9 5a 3a 01 00 01 00 01 00 # 0x00000485
|
||||
02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000495
|
||||
00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 # 0x000004a5
|
||||
00 00 00 00 00 00 00 00 # 0x000004b5
|
||||
|
||||
# offset 0x000004bd: page record, payload 119 bytes
|
||||
03 00 77 00 00 00 84 a6 e9 08 02 00 03 00 00 00 # 0x000004bd
|
||||
00 00 80 00 c0 00 00 02 00 00 00 08 00 00 03 00 # 0x000004cd
|
||||
00 00 00 00 00 00 00 72 00 00 00 00 00 00 00 00 # 0x000004dd
|
||||
00 00 00 00 00 00 00 6e 00 00 00 00 00 00 00 03 # 0x000004ed
|
||||
00 00 00 00 00 00 00 00 61 00 00 00 00 00 00 00 # 0x000004fd
|
||||
00 00 00 00 00 00 00 00 74 00 00 00 00 00 00 00 # 0x0000050d
|
||||
03 00 00 00 00 00 00 00 00 65 00 00 00 00 00 00 # 0x0000051d
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x0000052d
|
||||
00 # 0x0000053d
|
||||
|
||||
# offset 0x0000053e: ready record, payload 32 bytes
|
||||
05 00 20 00 00 00 29 ce 51 e1 28 ab 80 18 19 00 # 0x0000053e
|
||||
ef b3 15 fa 9d 03 c3 42 7f 77 2d f3 0c 00 87 cc # 0x0000054e
|
||||
a1 dc ac ce 6c fc fb 27 66 e1 # 0x0000055e
|
||||
|
||||
# offset 0x00000568: history record, payload 16 bytes
|
||||
04 00 10 00 00 00 cc 85 a3 b6 00 00 02 00 00 00 # 0x00000568
|
||||
04 00 00 00 00 00 00 00 00 00 # 0x00000578
|
||||
|
||||
# offset 0x00000582: page record, payload 86 bytes
|
||||
03 00 56 00 00 00 52 3b 6e 8d 02 00 02 00 00 00 # 0x00000582
|
||||
00 00 10 00 c0 00 00 04 00 00 00 08 00 00 00 00 # 0x00000592
|
||||
00 00 00 00 00 00 00 42 00 00 00 00 00 00 00 00 # 0x000005a2
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x000005b2
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x000005c2
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x000005d2
|
||||
|
||||
# offset 0x000005e2: page record, payload 86 bytes
|
||||
03 00 56 00 00 00 fb bc 15 06 02 00 02 00 00 00 # 0x000005e2
|
||||
00 00 10 00 c0 00 00 04 00 00 00 08 00 00 00 00 # 0x000005f2
|
||||
00 00 00 00 00 00 00 41 00 00 00 00 00 00 00 00 # 0x00000602
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000612
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000622
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000632
|
||||
|
||||
# offset 0x00000642: history record, payload 16 bytes
|
||||
04 00 10 00 00 00 39 57 cc cf 01 00 00 00 00 00 # 0x00000642
|
||||
00 00 00 00 00 00 00 00 00 00 # 0x00000652
|
||||
|
||||
# offset 0x0000065c: finish record, payload 32 bytes
|
||||
06 00 20 00 00 00 05 71 15 22 3e 4d e9 7a c1 02 # 0x0000065c
|
||||
0b a3 6a 10 a0 88 1c 07 03 2c 68 52 15 be 76 d0 # 0x0000066c
|
||||
4c a9 a6 2b fd f4 ac 49 62 2b # 0x0000067c
|
||||
7
src/terminal/snapshot/testdata/envelope-v1.hex
vendored
Normal file
7
src/terminal/snapshot/testdata/envelope-v1.hex
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Ghostty snapshot fixture
|
||||
# Wire version: 1
|
||||
# Generated by its snapshot test; review before replacing.
|
||||
# On mismatch, the candidate is copied to the repository root.
|
||||
|
||||
# offset 0x00000000: encoded bytes
|
||||
47 48 4f 53 54 53 4e 50 01 00 # 0x00000000
|
||||
7
src/terminal/snapshot/testdata/history-header-v1.hex
vendored
Normal file
7
src/terminal/snapshot/testdata/history-header-v1.hex
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Ghostty snapshot fixture
|
||||
# Wire version: 1
|
||||
# Generated by its snapshot test; review before replacing.
|
||||
# On mismatch, the candidate is copied to the repository root.
|
||||
|
||||
# offset 0x00000000: encoded bytes
|
||||
01 00 04 03 02 01 08 07 06 05 04 03 02 01 0a 09 # 0x00000000
|
||||
7
src/terminal/snapshot/testdata/hyperlink-explicit-v1.hex
vendored
Normal file
7
src/terminal/snapshot/testdata/hyperlink-explicit-v1.hex
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Ghostty snapshot fixture
|
||||
# Wire version: 1
|
||||
# Generated by its snapshot test; review before replacing.
|
||||
# On mismatch, the candidate is copied to the repository root.
|
||||
|
||||
# offset 0x00000000: encoded bytes
|
||||
02 02 00 00 00 69 64 03 00 00 00 75 72 69 # 0x00000000
|
||||
7
src/terminal/snapshot/testdata/hyperlink-implicit-v1.hex
vendored
Normal file
7
src/terminal/snapshot/testdata/hyperlink-implicit-v1.hex
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Ghostty snapshot fixture
|
||||
# Wire version: 1
|
||||
# Generated by its snapshot test; review before replacing.
|
||||
# On mismatch, the candidate is copied to the repository root.
|
||||
|
||||
# offset 0x00000000: encoded bytes
|
||||
01 04 03 02 01 03 00 00 00 75 72 69 # 0x00000000
|
||||
9
src/terminal/snapshot/testdata/page-empty-record-v1.hex
vendored
Normal file
9
src/terminal/snapshot/testdata/page-empty-record-v1.hex
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# Ghostty snapshot fixture
|
||||
# Wire version: 1
|
||||
# Generated by its snapshot test; review before replacing.
|
||||
# On mismatch, the candidate is copied to the repository root.
|
||||
|
||||
# offset 0x00000000: encoded bytes
|
||||
03 00 25 00 00 00 8c 05 d6 d3 01 00 01 00 00 00 # 0x00000000
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000010
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000020
|
||||
8
src/terminal/snapshot/testdata/page-header-v1.hex
vendored
Normal file
8
src/terminal/snapshot/testdata/page-header-v1.hex
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Ghostty snapshot fixture
|
||||
# Wire version: 1
|
||||
# Generated by its snapshot test; review before replacing.
|
||||
# On mismatch, the candidate is copied to the repository root.
|
||||
|
||||
# offset 0x00000000: encoded bytes
|
||||
02 01 04 03 06 05 08 07 0a 09 0c 0b 10 0f 0e 0d # 0x00000000
|
||||
14 13 12 11 # 0x00000010
|
||||
21
src/terminal/snapshot/testdata/page-v1.hex
vendored
Normal file
21
src/terminal/snapshot/testdata/page-v1.hex
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Ghostty snapshot fixture
|
||||
# Wire version: 1
|
||||
# Generated by its snapshot test; review before replacing.
|
||||
# On mismatch, the candidate is copied to the repository root.
|
||||
|
||||
# offset 0x00000000: PAGE header
|
||||
03 00 02 00 02 00 02 00 08 00 00 02 80 00 00 00 # 0x00000000
|
||||
00 01 00 00 # 0x00000010
|
||||
|
||||
# offset 0x00000014: style and hyperlink tables, rows, and cells
|
||||
01 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 # 0x00000014
|
||||
00 00 03 00 00 00 00 00 01 2a 00 00 00 00 00 00 # 0x00000024
|
||||
00 00 00 00 01 00 02 01 00 00 00 61 05 00 00 00 # 0x00000034
|
||||
61 6c 70 68 61 03 00 01 04 03 02 01 04 00 00 00 # 0x00000044
|
||||
62 65 74 61 04 00 01 05 00 01 00 01 00 41 00 00 # 0x00000054
|
||||
00 00 00 00 00 00 02 02 00 03 00 03 00 00 00 00 # 0x00000064
|
||||
00 00 00 00 00 01 00 00 00 01 00 01 00 07 00 00 # 0x00000074
|
||||
00 00 00 00 00 0b 00 00 00 00 00 00 00 00 78 00 # 0x00000084
|
||||
00 00 02 00 00 00 01 03 00 00 02 03 00 00 02 00 # 0x00000094
|
||||
01 00 00 00 00 00 aa bb cc 00 00 00 00 00 00 03 # 0x000000a4
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x000000b4
|
||||
7
src/terminal/snapshot/testdata/record-page-header-v1.hex
vendored
Normal file
7
src/terminal/snapshot/testdata/record-page-header-v1.hex
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Ghostty snapshot fixture
|
||||
# Wire version: 1
|
||||
# Generated by its snapshot test; review before replacing.
|
||||
# On mismatch, the candidate is copied to the repository root.
|
||||
|
||||
# offset 0x00000000: encoded bytes
|
||||
03 00 18 00 00 00 1b 44 78 71 # 0x00000000
|
||||
9
src/terminal/snapshot/testdata/screen-header-v1.hex
vendored
Normal file
9
src/terminal/snapshot/testdata/screen-header-v1.hex
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# Ghostty snapshot fixture
|
||||
# Wire version: 1
|
||||
# Generated by its snapshot test; review before replacing.
|
||||
# On mismatch, the candidate is copied to the repository root.
|
||||
|
||||
# offset 0x00000000: encoded bytes
|
||||
01 00 03 02 05 04 07 06 03 19 00 00 00 00 01 7f # 0x00000000
|
||||
00 00 02 12 34 56 ff 03 00 00 0d 0c 0b 0a e4 3d # 0x00000010
|
||||
02 07 01 02 04 08 10 1f 00 11 02 03 01 # 0x00000020
|
||||
8
src/terminal/snapshot/testdata/screen-saved-cursor-v1.hex
vendored
Normal file
8
src/terminal/snapshot/testdata/screen-saved-cursor-v1.hex
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Ghostty snapshot fixture
|
||||
# Wire version: 1
|
||||
# Generated by its snapshot test; review before replacing.
|
||||
# On mismatch, the candidate is copied to the repository root.
|
||||
|
||||
# offset 0x00000000: encoded bytes
|
||||
02 01 04 03 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000000
|
||||
00 00 00 00 07 e4 3d # 0x00000010
|
||||
7
src/terminal/snapshot/testdata/style-v1.hex
vendored
Normal file
7
src/terminal/snapshot/testdata/style-v1.hex
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Ghostty snapshot fixture
|
||||
# Wire version: 1
|
||||
# Generated by its snapshot test; review before replacing.
|
||||
# On mismatch, the candidate is copied to the repository root.
|
||||
|
||||
# offset 0x00000000: encoded bytes
|
||||
00 00 00 00 01 7f 00 00 02 12 34 56 ff 03 00 00 # 0x00000000
|
||||
13
src/terminal/snapshot/testdata/terminal-header-v1.hex
vendored
Normal file
13
src/terminal/snapshot/testdata/terminal-header-v1.hex
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Ghostty snapshot fixture
|
||||
# Wire version: 1
|
||||
# Generated by its snapshot test; review before replacing.
|
||||
# On mismatch, the candidate is copied to the repository root.
|
||||
|
||||
# offset 0x00000000: encoded bytes
|
||||
02 01 04 03 08 07 06 05 0c 0b 0a 09 01 00 02 00 # 0x00000000
|
||||
03 00 04 00 01 01 00 02 00 41 00 00 00 01 03 02 # 0x00000010
|
||||
02 01 04 04 01 21 01 01 00 00 00 00 00 00 00 00 # 0x00000020
|
||||
00 00 00 00 02 00 00 01 00 00 00 00 02 00 00 01 # 0x00000030
|
||||
01 02 03 00 00 00 00 00 00 00 00 01 04 05 06 01 # 0x00000040
|
||||
07 08 09 01 0a 0b 0c ff ff ff ff ff ff ff ff 08 # 0x00000050
|
||||
07 06 05 04 03 02 01 # 0x00000060
|
||||
Reference in New Issue
Block a user