⋯ 583 hidden lines@@ -584,3 +584,90 @@ func FuzzSessionNeverPanics(f *testing.F) { 584 584 _, _ = s.Finish() 585 585 }) 586 586 } 587 + 588 + func placeWithControls(t *testing.T, cellW, cellH int, imgW, imgH int, header string, chunked bool) graphics.PlacementView {589 + t.Helper() 590 + scene := graphics.NewScene(graphics.Limits{}) 591 + session := NewSession(scene) 592 + session.SetCellPixels(cellW, cellH) 593 + payload := base64.StdEncoding.EncodeToString(pngBytes(t, imgW, imgH)) 594 + if chunked { 595 + half := len(payload) / 2 / 4 * 4 596 + if _, err := session.Feed(apc(header+",m=1" , payload[:half])); err != nil { 597 + t.Fatal(err) 598 + } 599 + if _, err := session.Feed(apc("m=0" , payload[half:])); err != nil { 600 + t.Fatal(err) 601 + } 602 + } else if _, err := session.Feed(apc(header, payload)); err != nil { 603 + t.Fatal(err) 604 + } 605 + placements := scene.Snapshot().Placements() 606 + if len(placements) != 1 || scene.Snapshot().Usage().Assets != 1 { 607 + t.Fatalf("usage = %#v" , scene.Snapshot().Usage()) 608 + } 609 + return placements[0] 610 + } 611 + 612 + func TestPlacementSingleColumnOrRowDerivesOther(t *testing.T) {613 + tests := []struct { 614 + name string 615 + cellW, cellH int 616 + imgW, imgH int 617 + header string 618 + chunked bool 619 + cols, rows int64 620 + }{ 621 + {"columns only" , 10, 20, 4, 4, "a=T,f=100,C=1,c=5" , false , 5, 3}, 622 + {"rows only" , 10, 20, 4, 4, "a=T,f=100,C=1,r=3" , false , 6, 3}, 623 + {"both" , 10, 20, 4, 4, "a=T,f=100,C=1,c=5,r=3" , false , 5, 3}, 624 + {"wide image columns only" , 10, 20, 40, 10, "a=T,f=100,C=1,c=8" , false , 8, 1}, 625 + {"crop columns only" , 10, 20, 8, 8, "a=T,f=100,C=1,c=4,w=8,h=4" , false , 4, 1}, 626 + {"crop rows only" , 10, 20, 8, 8, "a=T,f=100,C=1,r=2,w=8,h=4" , false , 8, 2}, 627 + {"unknown cell size assumes 1:2" , 0, 0, 4, 4, "a=T,f=100,C=1,c=4" , false , 4, 2}, 628 + {"unknown cell size rows only" , 0, 0, 4, 4, "a=T,f=100,C=1,r=2" , false , 4, 2}, 629 + {"minimum one" , 10, 20, 100, 1, "a=T,f=100,C=1,c=2" , false , 2, 1}, 630 + {"zero means unset" , 10, 20, 4, 4, "a=T,f=100,C=1,c=5,r=0" , false , 5, 3}, 631 + {"chunked rows only" , 10, 20, 4, 4, "a=T,f=100,C=1,r=3" , true , 6, 3}, 632 + } 633 + for _, tt := range tests { 634 + t.Run(tt.name, func (t *testing.T) { 635 + p := placeWithControls(t, tt.cellW, tt.cellH, tt.imgW, tt.imgH, tt.header, tt.chunked) 636 + cells := p.Cells() 637 + if cells.Width != tt.cols || cells.Height != tt.rows { 638 + t.Fatalf("cells = %dx%d, want %dx%d" , cells.Width, cells.Height, tt.cols, tt.rows) 639 + } 640 + }) 641 + } 642 + } 643 + 644 + func TestPlacementZeroColumnsAndRowsMeansAuto(t *testing.T) {645 + p := placeWithControls(t, 10, 20, 4, 4, "a=T,f=100,C=1,c=0,r=0" , false ) 646 + if !p.Cells().Empty() { 647 + t.Fatalf("cells = %#v, want none" , p.Cells()) 648 + } 649 + } 650 + 651 + func TestDeriveCells(t *testing.T) {652 + const maxInt64 = int64(^uint64(0) >> 1) 653 + tests := []struct { 654 + name string 655 + given, givenSrc, otherSrc, givenCell, otherCell int64 656 + want int64 657 + wantOK bool 658 + }{ 659 + {"exact division" , 4, 4, 4, 10, 20, 2, true }, 660 + {"rounds up" , 5, 4, 4, 10, 20, 3, true }, 661 + {"tiny result is at least one" , 1, 1000, 1, 1, 1, 1, true }, 662 + {"overflow" , maxInt64, 1, 1000, 1000, 1, 0, false }, 663 + {"zero source" , 5, 0, 4, 10, 20, 0, false }, 664 + } 665 + for _, tt := range tests { 666 + t.Run(tt.name, func (t *testing.T) { 667 + got, ok := deriveCells(tt.given, tt.givenSrc, tt.otherSrc, tt.givenCell, tt.otherCell) 668 + if got != tt.want || ok != tt.wantOK { 669 + t.Fatalf("deriveCells = %d, %v; want %d, %v" , got, ok, tt.want, tt.wantOK) 670 + } 671 + }) 672 + } 673 + }