a73x

b8e8ca5a

test: cover redraw erasure and protected terminal state

a73x   2026-09-05 05:20

Commit message
test: cover redraw erasure and protected terminal state

src/engine/engine.zig
Old New
@@ -1805,3 +1805,89 @@ test "binary output bounded deterministic flood keeps every encoded viewport rep
1805 } 1805 }
1806 } 1806 }
1807 } 1807 }
1808
1809 test "erase and shorter redraw replace styled wide tails through live deltas" {
1810 const alloc = std.testing.allocator;
1811 var e = try Engine.init(alloc, .{ .cols = 36, .rows = 6 });
1812 defer e.deinit();
1813 // Plural, off-origin rows with style and wide spacers: clearing only the
1814 // text, keeping an old tail, or missing a changed empty row all show up.
1815 e.feed("\x1b[2;5H\x1b[1;31;44mLONG 漢字 WIDE TAIL\x1b[5;8HSECOND 漢字 TAIL\x1b[0m");
1816 var tracker: delta.DeltaTracker = .{};
1817 defer tracker.deinit(alloc);
1818 try tracker.rebuild(alloc, e, 6, 36);
1819 const g = try Grid.init(alloc, 36, 6);
1820 defer g.deinit();
1821 var replica = @import("term").replica.Replica.init(alloc, g);
1822 const snapshot = try delta.buildSnapshot(alloc, e, .{ .seq = tracker.seq, .history_rows = 0, .cols = 36, .rows = 6, .epoch = 93 });
1823 defer alloc.free(snapshot);
1824 _ = try replica.apply(.snapshot, snapshot);
1825 try std.testing.expect(g.row(1).cells[4].style.flags != 0);
1826 try std.testing.expectEqual(proto.Wide.wide, g.row(4).cells[14].wide);
1827
1828 const actions = [_][]const u8{
1829 "\x1b[2;5Hshort\x1b[K\x1b[5;8Htiny\x1b[K",
1830 "\x1b[H\x1b[2J\x1b[3J",
1831 };
1832 for (actions, 0..) |action, step| {
1833 const since = replica.last_seq;
1834 e.feed(action);
1835 try std.testing.expectEqual(delta.DeltaTracker.Update.advanced, try tracker.update(alloc, e));
1836 const payload = try tracker.buildDeltaSince(alloc, e, since);
1837 defer alloc.free(payload);
1838 try std.testing.expectEqual(@import("term").replica.Replica.Applied.painted, try replica.apply(.delta, payload));
1839 const dump = try g.dumpPlain(alloc);
1840 defer alloc.free(dump);
1841 if (step == 0) {
1842 try std.testing.expectEqualStrings("\n short\n\n\n tiny", dump);
1843 } else try std.testing.expectEqualStrings("", dump);
1844 for (g.lines, 0..) |row, y| {
1845 for (row.cells, 0..) |cell, x| {
1846 const retained_text = step == 0 and ((y == 1 and x >= 4 and x < 9) or (y == 4 and x >= 7 and x < 11));
1847 try std.testing.expect(cell.style.isDefault());
1848 try std.testing.expectEqual(proto.Wide.narrow, cell.wide);
1849 if (!retained_text) try std.testing.expect(cell.text_len == 0 or std.mem.eql(u8, row.textOf(cell), " "));
1850 }
1851 }
1852 }
1853 }
1854
1855 test "erase preserves ISO protected text until terminal reset and deltas mirror that state" {
1856 const alloc = std.testing.allocator;
1857 var e = try Engine.init(alloc, .{ .cols = 36, .rows = 6 });
1858 defer e.deinit();
1859 var tracker: delta.DeltaTracker = .{};
1860 defer tracker.deinit(alloc);
1861 try tracker.rebuild(alloc, e, 6, 36);
1862 const g = try Grid.init(alloc, 36, 6);
1863 defer g.deinit();
1864 var replica = @import("term").replica.Replica.init(alloc, g);
1865 const snapshot = try delta.buildSnapshot(alloc, e, .{ .seq = tracker.seq, .history_rows = 0, .cols = 36, .rows = 6, .epoch = 93 });
1866 defer alloc.free(snapshot);
1867 _ = try replica.apply(.snapshot, snapshot);
1868
1869 // Binary output can contain SPA (ESC V). EPA (ESC W) stops protecting
1870 // new characters but does not remove protection from the existing cells.
1871 // Their survival after ED/EL is authoritative terminal state, not stale
1872 // replica or framebuffer content. RIS resets that state and clears them.
1873 const actions = [_]struct { bytes: []const u8, expected: []const u8 }{
1874 .{ .bytes = "\x1b[2;5H\x1bVPROTECTED-FIRST\x1bW\x1b[5;8H\x1bVPROTECTED-SECOND\x1bW", .expected = "\n PROTECTED-FIRST\n\n\n PROTECTED-SECOND" },
1875 .{ .bytes = "\x1b[H\x1b[2J\x1b[3J", .expected = "\n PROTECTED-FIRST\n\n\n PROTECTED-SECOND" },
1876 .{ .bytes = "\x1b[2;5Hnew\x1b[K\x1b[5;8Hx\x1b[K", .expected = "\n newTECTED-FIRST\n\n\n xROTECTED-SECOND" },
1877 .{ .bytes = "\x1bc", .expected = "" },
1878 };
1879 for (actions) |action| {
1880 const since = replica.last_seq;
1881 e.feed(action.bytes);
1882 try std.testing.expectEqual(delta.DeltaTracker.Update.advanced, try tracker.update(alloc, e));
1883 const payload = try tracker.buildDeltaSince(alloc, e, since);
1884 defer alloc.free(payload);
1885 try std.testing.expectEqual(@import("term").replica.Replica.Applied.painted, try replica.apply(.delta, payload));
1886 const dump = try g.dumpPlain(alloc);
1887 defer alloc.free(dump);
1888 try std.testing.expectEqualStrings(action.expected, dump);
1889 }
1890 for (g.lines) |row| {
1891 for (row.cells) |cell| try std.testing.expect(cell.isBlank());
1892 }
1893 }