Real-image posters: a new size is a new image id, never a changed placement
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
+48
-57
@@ -51,16 +51,21 @@ func TestKittyUploadChunksAndEncodes(t *testing.T) {
|
||||
k := newKittyGraphics(rec.write)
|
||||
img := noisy(200, 300) // incompressible: far more than one 4096-byte chunk of base64
|
||||
|
||||
id, err := k.imageID(img, img.Bounds())
|
||||
id, err := k.show(img, img.Bounds(), 20, 15)
|
||||
if err != nil || id != 1 {
|
||||
t.Fatalf("id=%d err=%v", id, err)
|
||||
}
|
||||
if len(rec.cmds) < 3 {
|
||||
if len(rec.cmds) < 4 {
|
||||
t.Fatalf("only %d commands for a large image", len(rec.cmds))
|
||||
}
|
||||
// The upload is followed by the placement, once.
|
||||
if last := rec.cmds[len(rec.cmds)-1]; last != "\x1b_Ga=p,U=1,i=1,c=20,r=15,q=2\x1b\\" {
|
||||
t.Errorf("last command = %q", last)
|
||||
}
|
||||
uploads := rec.cmds[:len(rec.cmds)-1]
|
||||
re := regexp.MustCompile(`^\x1b_G([^;]*);(.*)\x1b\\$`)
|
||||
var payload strings.Builder
|
||||
for i, c := range rec.cmds {
|
||||
for i, c := range uploads {
|
||||
m := re.FindStringSubmatch(c)
|
||||
if m == nil {
|
||||
t.Fatalf("command %d is not a graphics command: %q", i, c[:min(40, len(c))])
|
||||
@@ -69,7 +74,7 @@ func TestKittyUploadChunksAndEncodes(t *testing.T) {
|
||||
if len(data) > kittyChunk {
|
||||
t.Errorf("chunk %d has %d bytes", i, len(data))
|
||||
}
|
||||
last := i == len(rec.cmds)-1
|
||||
last := i == len(uploads)-1
|
||||
switch {
|
||||
case i == 0 && !strings.HasPrefix(keys, "a=t,f=100,t=d,i=1,q=2,m=1"):
|
||||
t.Errorf("first command keys = %q", keys)
|
||||
@@ -94,43 +99,51 @@ func TestKittyUploadChunksAndEncodes(t *testing.T) {
|
||||
t.Error("pixel data changed")
|
||||
}
|
||||
|
||||
// The same image is not uploaded twice; another one gets the next id.
|
||||
// The same footprint is not uploaded twice; another poster gets the next id.
|
||||
n := len(rec.cmds)
|
||||
if again, _ := k.imageID(img, img.Bounds()); again != id || len(rec.cmds) != n {
|
||||
t.Error("an uploaded image must be reused")
|
||||
if again, _ := k.show(img, img.Bounds(), 20, 15); again != id || len(rec.cmds) != n {
|
||||
t.Error("an uploaded footprint must be reused")
|
||||
}
|
||||
if other, _ := k.imageID(noisy(4, 4), image.Rect(0, 0, 4, 4)); other != 2 {
|
||||
small := noisy(4, 4)
|
||||
if other, _ := k.show(small, small.Bounds(), 3, 2); other != 2 {
|
||||
t.Errorf("second image id = %d", other)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKittyPlacement(t *testing.T) {
|
||||
func TestKittyNewSizeIsANewImage(t *testing.T) {
|
||||
rec := &recorder{}
|
||||
k := newKittyGraphics(rec.write)
|
||||
if err := k.place(7, 20, 15); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rec.cmds) != 1 || rec.cmds[0] != "\x1b_Ga=p,U=1,i=7,c=20,r=15,q=2\x1b\\" {
|
||||
t.Fatalf("first placement: %q", rec.cmds)
|
||||
}
|
||||
k.place(7, 20, 15) // unchanged: nothing to send
|
||||
if len(rec.cmds) != 1 {
|
||||
t.Errorf("unchanged placement sent %d commands", len(rec.cmds))
|
||||
}
|
||||
k.place(7, 10, 8) // a new size replaces the placement
|
||||
if len(rec.cmds) != 3 || rec.cmds[1] != "\x1b_Ga=d,d=i,i=7,q=2\x1b\\" || rec.cmds[2] != "\x1b_Ga=p,U=1,i=7,c=10,r=8,q=2\x1b\\" {
|
||||
t.Errorf("resize: %q", rec.cmds[1:])
|
||||
img := noisy(10, 15)
|
||||
|
||||
id1, _ := k.show(img, img.Bounds(), 20, 15)
|
||||
rec.cmds = nil
|
||||
if again, _ := k.show(img, img.Bounds(), 20, 15); again != id1 || len(rec.cmds) != 0 {
|
||||
t.Fatalf("same footprint: id %d, %d commands", again, len(rec.cmds))
|
||||
}
|
||||
|
||||
rec.cmds = nil
|
||||
small := noisy(2, 2)
|
||||
id, _ := k.imageID(small, small.Bounds())
|
||||
k.deleteAll()
|
||||
if last := rec.cmds[len(rec.cmds)-1]; last != "\x1b_Ga=d,d=I,i="+itoa(int(id))+",q=2\x1b\\" {
|
||||
t.Errorf("deleteAll sent %q", last)
|
||||
// A different size is never done by changing the placement (a terminal can keep the
|
||||
// old one): the poster is uploaded under a new id, placed, and the old id removed.
|
||||
id2, _ := k.show(img, img.Bounds(), 10, 8)
|
||||
if id2 == id1 {
|
||||
t.Fatal("a new footprint must get a new id")
|
||||
}
|
||||
if len(k.ids) != 0 || len(k.placed) != 0 {
|
||||
t.Error("deleteAll must forget everything")
|
||||
all := strings.Join(rec.cmds, "")
|
||||
if !strings.Contains(all, "a=t,f=100,t=d,i=2") || !strings.Contains(all, "a=p,U=1,i=2,c=10,r=8") || !strings.Contains(all, "a=d,d=I,i=1") {
|
||||
t.Errorf("resize sent %q", rec.cmds)
|
||||
}
|
||||
if strings.Contains(all, "a=p,U=1,i=1") {
|
||||
t.Error("the old placement must not be touched")
|
||||
}
|
||||
|
||||
// After deleteAll (what a window resize triggers) everything is uploaded afresh.
|
||||
rec.cmds = nil
|
||||
k.deleteAll()
|
||||
id3, _ := k.show(img, img.Bounds(), 10, 8)
|
||||
if id3 == id2 || !strings.Contains(strings.Join(rec.cmds, ""), "a=d,d=I,i=2") || !strings.Contains(strings.Join(rec.cmds, ""), "a=t,f=100,t=d,i=3") {
|
||||
t.Errorf("after deleteAll: id %d, %q", id3, rec.cmds)
|
||||
}
|
||||
if len(k.ids) != 1 || len(k.latest) != 1 {
|
||||
t.Errorf("bookkeeping: %d ids, %d latest", len(k.ids), len(k.latest))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,15 +256,11 @@ func TestCoverViewGraphicsDrawsPlaceholders(t *testing.T) {
|
||||
// A cell aspect of 2.5 (taller cells) changes the footprint of the same box.
|
||||
c.cellAspect = func() float64 { return 2.5 }
|
||||
c.Draw(screen)
|
||||
// The box is now 17×10 cells, a different shape: a new crop is uploaded, the
|
||||
// old upload is removed, and the image is placed afresh.
|
||||
var placements []string
|
||||
for _, cmd := range rec.cmds[2:] {
|
||||
placements = append(placements, cmd[:min(len(cmd), 30)])
|
||||
}
|
||||
last := rec.cmds[len(rec.cmds)-1]
|
||||
if !strings.Contains(last, "c=17,r=10") || !strings.Contains(strings.Join(rec.cmds[2:], ""), "a=d,d=I,i=1") {
|
||||
t.Errorf("after the cell aspect changed: %q", placements)
|
||||
// The box is now 17×10 cells, a different shape: a new image id is uploaded and
|
||||
// placed, the old upload is removed.
|
||||
all := strings.Join(rec.cmds[2:], "")
|
||||
if !strings.Contains(all, "a=p,U=1,i=2,c=17,r=10") || !strings.Contains(all, "a=d,d=I,i=1") {
|
||||
t.Errorf("after the cell aspect changed: %q", rec.cmds[2:])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,24 +289,6 @@ func TestCropToBox(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidateReplacesPlacements(t *testing.T) {
|
||||
rec := &recorder{}
|
||||
k := newKittyGraphics(rec.write)
|
||||
k.place(3, 17, 13)
|
||||
k.place(4, 10, 8)
|
||||
rec.cmds = nil
|
||||
k.invalidate()
|
||||
if len(rec.cmds) != 2 || !strings.Contains(strings.Join(rec.cmds, ""), "a=d,d=i,i=3") || !strings.Contains(strings.Join(rec.cmds, ""), "a=d,d=i,i=4") {
|
||||
t.Fatalf("invalidate sent %q", rec.cmds)
|
||||
}
|
||||
// The same size is placed again afterwards, since the placement is gone.
|
||||
rec.cmds = nil
|
||||
k.place(3, 17, 13)
|
||||
if len(rec.cmds) != 1 || !strings.Contains(rec.cmds[0], "a=p,U=1,i=3,c=17,r=13") {
|
||||
t.Errorf("after invalidate: %q", rec.cmds)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCellAspectFromWindow(t *testing.T) {
|
||||
// 140×50 cells of 9×20 px plus 4 and 11 px of padding: plain division
|
||||
// would say 1264/140=9.03 and 1011/50=20.22 (aspect 2.24), the real shape is 20/9.
|
||||
|
||||
Reference in New Issue
Block a user