package imageprocessor import ( "bytes" "errors" "testing" ) // TestImageProcessor_RefusesComputedSideAboveMaximum verifies that a size // with one side 0, which Process computes from the input's proportions, is // served when that side comes out at MaxOutputDimension and refused with // ErrOutputTooLarge when it would be above. A 4096x1 or 1x4096 input given // 2 for its short side has 8192 for its long side. func TestImageProcessor_RefusesComputedSideAboveMaximum(t *testing.T) { t.Parallel() tests := []struct { name string inputWidth, inputHeight int size Size wantErr error }{ {"width at the maximum", 4096, 1, Size{Height: 2}, nil}, {"width above the maximum", 4096, 1, Size{Height: 3}, ErrOutputTooLarge}, {"height at the maximum", 1, 4096, Size{Width: 2}, nil}, {"height above the maximum", 1, 4096, Size{Width: 3}, ErrOutputTooLarge}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() input := createTestJPEG(t, tt.inputWidth, tt.inputHeight) result, err := New(Params{}).Process(t.Context(), bytes.NewReader(input), &Request{Size: tt.size, Format: FormatJPEG, FitMode: FitCover}) if result != nil { t.Logf("Process() made %dx%d", result.Width, result.Height) if max(result.Width, result.Height) != MaxOutputDimension { t.Errorf("Process() made %dx%d, want a side of %d", result.Width, result.Height, MaxOutputDimension) } } if !errors.Is(err, tt.wantErr) { t.Errorf("Process() error = %v, want %v", err, tt.wantErr) } }) } } // TestImageProcessor_ComputedSideIsAtLeastOne verifies that, for a size with // one side 0, the side computed from the input's proportions is 1 where it // would round down to 0, and that Process makes that size in every fit mode // but FitOutside, which scales the input to cover the size and so can make // it longer. Each case checks the size targetDimensions gives before it // calls Process: with that side at 0, an 8192x1 input at 1300x0 makes // libvips ask for about 39 GB with FitCover. func TestImageProcessor_ComputedSideIsAtLeastOne(t *testing.T) { t.Parallel() tests := []struct { name string inputWidth, inputHeight int size Size wantWidth, wantHeight int }{ {"8192x1 at 100x0", 8192, 1, Size{Width: 100}, 100, 1}, {"8192x1 at 1300x0", 8192, 1, Size{Width: 1300}, 1300, 1}, {"8192x2 at 100x0", 8192, 2, Size{Width: 100}, 100, 1}, {"1x8192 at 0x100", 1, 8192, Size{Height: 100}, 1, 100}, {"1x8192 at 0x1300", 1, 8192, Size{Height: 1300}, 1, 1300}, {"2x8192 at 0x100", 2, 8192, Size{Height: 100}, 1, 100}, } fits := []FitMode{FitCover, FitContain, FitFill, FitInside, FitOutside} for _, tt := range tests { for _, fit := range fits { t.Run(tt.name+" "+string(fit), func(t *testing.T) { t.Parallel() width, height, err := targetDimensions(tt.size, tt.inputWidth, tt.inputHeight) if err != nil || width != tt.wantWidth || height != tt.wantHeight { t.Fatalf("targetDimensions() = %dx%d, %v, want %dx%d", width, height, err, tt.wantWidth, tt.wantHeight) } input := createTestJPEG(t, tt.inputWidth, tt.inputHeight) result, err := New(Params{}).Process(t.Context(), bytes.NewReader(input), &Request{Size: tt.size, Format: FormatJPEG, FitMode: fit}) if err != nil { t.Fatalf("Process() error = %v", err) } defer func() { _ = result.Content.Close() }() t.Logf("Process() made %dx%d", result.Width, result.Height) ok := result.Width == tt.wantWidth && result.Height == tt.wantHeight if fit == FitOutside { ok = result.Width >= tt.wantWidth && result.Height >= tt.wantHeight } if !ok { t.Errorf("Process() made %dx%d, want %dx%d (at least, for %s)", result.Width, result.Height, tt.wantWidth, tt.wantHeight, FitOutside) } }) } } }