Files
upstream-mirror 240db4e2a7
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 00:29:55 +05:00

256 lines
6.2 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package imaging
import (
"bytes"
"image"
"image/color"
"os"
"path/filepath"
"testing"
"github.com/mattermost/mattermost/server/v8/channels/utils/fileutils"
"github.com/stretchr/testify/require"
)
// requireSameImage asserts that want and got cover the same bounds and have
// identical RGBA values at every pixel. We compare decoded pixels rather than
// re-encoded byte streams because image/png is not byte-stable across Go
// versions even for identical pixel content. On mismatch we walk every pixel
// before failing so the report includes the total diff rate, not just the
// first divergence.
func requireSameImage(t *testing.T, want, got image.Image) {
t.Helper()
require.Equal(t, want.Bounds(), got.Bounds())
b := got.Bounds()
total := b.Dx() * b.Dy()
var (
diff int
firstX int
firstY int
firstWant [4]uint32
firstGot [4]uint32
)
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
wr, wg, wb, wa := want.At(x, y).RGBA()
gr, gg, gb, ga := got.At(x, y).RGBA()
if wr == gr && wg == gg && wb == gb && wa == ga {
continue
}
if diff == 0 {
firstX, firstY = x, y
firstWant = [4]uint32{wr, wg, wb, wa}
firstGot = [4]uint32{gr, gg, gb, ga}
}
diff++
}
}
if diff > 0 {
t.Fatalf("%d / %d pixels differ (%.2f%%); first at (%d, %d): want %v got %v",
diff, total, 100*float64(diff)/float64(total),
firstX, firstY, firstWant, firstGot)
}
}
func TestFillImageTransparency(t *testing.T) {
tcs := []struct {
name string
inputName string
outputName string
fillColor color.Color
}{
{
"8-bit Palette",
"fill_test_8bit_palette.png",
"fill_test_8bit_palette_out.png",
color.RGBA{0, 255, 0, 255},
},
{
"8-bit RGB",
"fill_test_8bit_rgb.png",
"fill_test_8bit_rgb_out.png",
color.RGBA{0, 255, 0, 255},
},
{
"8-bit RGBA",
"fill_test_8bit_rgba.png",
"fill_test_8bit_rgba_out.png",
color.RGBA{0, 255, 0, 255},
},
{
"16-bit RGB",
"fill_test_16bit_rgb.png",
"fill_test_16bit_rgb_out.png",
color.RGBA{0, 255, 0, 255},
},
{
"16-bit RGBA",
"fill_test_16bit_rgba.png",
"fill_test_16bit_rgba_out.png",
color.RGBA{0, 255, 0, 255},
},
}
imgDir, ok := fileutils.FindDir("tests")
require.True(t, ok)
e, err := NewEncoder(EncoderOptions{})
require.NotNil(t, e)
require.NoError(t, err)
d, err := NewDecoder(DecoderOptions{})
require.NotNil(t, d)
require.NoError(t, err)
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
inputFile, err := os.Open(imgDir + "/" + tc.inputName)
require.NoError(t, err)
require.NotNil(t, inputFile)
defer func() {
require.NoError(t, inputFile.Close())
}()
inputImg, format, err := d.Decode(inputFile)
require.NoError(t, err)
require.NotNil(t, inputImg)
require.Equal(t, "png", format)
expectedBytes, err := os.ReadFile(imgDir + "/" + tc.outputName)
require.NoError(t, err)
FillImageTransparency(inputImg, tc.fillColor)
var b bytes.Buffer
err = e.EncodePNG(&b, inputImg)
require.NoError(t, err)
require.Equal(t, expectedBytes, b.Bytes())
})
}
t.Run("Opaque image", func(t *testing.T) {
inputFile, err := os.Open(imgDir + "/fill_test_opaque.png")
require.NoError(t, err)
require.NotNil(t, inputFile)
defer func() {
require.NoError(t, inputFile.Close())
}()
inputImg, format, err := d.Decode(inputFile)
require.NoError(t, err)
require.NotNil(t, inputImg)
require.Equal(t, "png", format)
_, err = inputFile.Seek(0, 0)
require.NoError(t, err)
expectedImg, format, err := d.Decode(inputFile)
require.NoError(t, err)
require.NotNil(t, expectedImg)
require.Equal(t, "png", format)
FillImageTransparency(inputImg, color.RGBA{0, 255, 0, 255})
require.Equal(t, expectedImg, inputImg)
})
}
func TestFillCenter(t *testing.T) {
tcs := []struct {
name string
outputName string
width int
height int
}{
{"100x100", "fill_test_output_100x100.png", 100, 100},
{"45x45", "fill_test_output_45x45.png", 45, 45},
{"100x45", "fill_test_output_100x45.png", 100, 45},
{"45x100", "fill_test_output_45x100.png", 45, 100},
}
imgDir, ok := fileutils.FindDir("tests")
require.True(t, ok)
d, err := NewDecoder(DecoderOptions{})
require.NoError(t, err)
require.NotNil(t, d)
inputFile, err := os.Open(filepath.Join(imgDir, "fill_test_input.png"))
require.NoError(t, err)
defer inputFile.Close()
inputImg, format, err := d.Decode(inputFile)
require.NoError(t, err)
require.Equal(t, "png", format)
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
expectedFile, err := os.Open(filepath.Join(imgDir, tc.outputName))
require.NoError(t, err)
defer expectedFile.Close()
expectedImg, format, err := d.Decode(expectedFile)
require.NoError(t, err)
require.Equal(t, "png", format)
out := FillCenter(inputImg, tc.width, tc.height)
requireSameImage(t, expectedImg, out)
})
}
}
func TestFit(t *testing.T) {
tcs := []struct {
name string
inputImg image.Image
maxW int
maxH int
expectedWidth int
expectedHeight int
}{
{
name: "no resize when smaller than bounds",
inputImg: image.NewRGBA(image.Rect(0, 0, 50, 50)),
maxW: 100,
maxH: 100,
expectedWidth: 50,
expectedHeight: 50,
},
{
name: "landscape clamps to width",
inputImg: image.NewRGBA(image.Rect(0, 0, 200, 100)),
maxW: 100,
maxH: 100,
expectedWidth: 100,
expectedHeight: 50,
},
{
name: "portrait clamps to height",
inputImg: image.NewRGBA(image.Rect(0, 0, 100, 200)),
maxW: 100,
maxH: 100,
expectedWidth: 50,
expectedHeight: 100,
},
{
name: "both dimensions exceed",
inputImg: image.NewRGBA(image.Rect(0, 0, 400, 200)),
maxW: 100,
maxH: 100,
expectedWidth: 100,
expectedHeight: 50,
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
out := Fit(tc.inputImg, tc.maxW, tc.maxH)
require.Equal(t, tc.expectedWidth, out.Bounds().Dx())
require.Equal(t, tc.expectedHeight, out.Bounds().Dy())
})
}
}