Skip to content

Commit 9cad8d3

Browse files
committed
images: Rework the golden tests
1 parent cfa0801 commit 9cad8d3

File tree

130 files changed

+332
-324
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

130 files changed

+332
-324
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
imports.*
44
dist/
55
public/
6+
.DS_Store

htesting/test_helpers.go

+5
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ func IsCI() bool {
110110
return (os.Getenv("CI") != "" || os.Getenv("CI_LOCAL") != "") && os.Getenv("CIRCLE_BRANCH") == ""
111111
}
112112

113+
// IsRealCI reports whether we're running in a CI server, but not in a local CI setup.
114+
func IsRealCI() bool {
115+
return IsCI() && os.Getenv("CI_LOCAL") == ""
116+
}
117+
113118
// IsGitHubAction reports whether we're running in a GitHub Action.
114119
func IsGitHubAction() bool {
115120
return os.Getenv("GITHUB_ACTION") != ""

hugolib/integrationtest_builder.go

+6
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ func TestOptWithOSFs() TestOpt {
105105
}
106106
}
107107

108+
func TestOptWithPrintAndKeepTempDir(b bool) TestOpt {
109+
return func(c *IntegrationTestConfig) {
110+
c.PrintAndKeepTempDir = b
111+
}
112+
}
113+
108114
// TestOptWithWorkingDir allows setting any config optiona as a function al option.
109115
func TestOptWithConfig(fn func(c *IntegrationTestConfig)) TestOpt {
110116
return func(c *IntegrationTestConfig) {

resources/image_test.go

-324
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,20 @@ package resources_test
1616
import (
1717
"context"
1818
"fmt"
19-
"image"
20-
"image/gif"
2119
"io/fs"
2220
"math/rand"
2321
"os"
24-
"path/filepath"
25-
"runtime"
2622
"strconv"
27-
"strings"
2823
"sync"
2924
"testing"
3025
"time"
3126

3227
"github.com/bep/imagemeta"
33-
"github.com/gohugoio/hugo/htesting"
34-
"github.com/gohugoio/hugo/resources/images/webp"
3528

36-
"github.com/gohugoio/hugo/common/hashing"
3729
"github.com/gohugoio/hugo/common/paths"
3830

3931
"github.com/spf13/afero"
4032

41-
"github.com/disintegration/gift"
42-
4333
"github.com/gohugoio/hugo/media"
4434
"github.com/gohugoio/hugo/resources/images"
4535
"github.com/google/go-cmp/cmp"
@@ -533,320 +523,6 @@ func BenchmarkImageExif(b *testing.B) {
533523
})
534524
}
535525

536-
// usesFMA indicates whether "fused multiply and add" (FMA) instruction is
537-
// used. The command "grep FMADD go/test/codegen/floats.go" can help keep
538-
// the FMA-using architecture list updated.
539-
var usesFMA = runtime.GOARCH == "s390x" ||
540-
runtime.GOARCH == "ppc64" ||
541-
runtime.GOARCH == "ppc64le" ||
542-
runtime.GOARCH == "arm64" ||
543-
runtime.GOARCH == "riscv64"
544-
545-
// goldenEqual compares two NRGBA images. It is used in golden tests only.
546-
// A small tolerance is allowed on architectures using "fused multiply and add"
547-
// (FMA) instruction to accommodate for floating-point rounding differences
548-
// with control golden images that were generated on amd64 architecture.
549-
// See https://golang.org/ref/spec#Floating_point_operators
550-
// and https://github.com/gohugoio/hugo/issues/6387 for more information.
551-
//
552-
// Borrowed from https://github.com/disintegration/gift/blob/a999ff8d5226e5ab14b64a94fca07c4ac3f357cf/gift_test.go#L598-L625
553-
// Copyright (c) 2014-2019 Grigory Dryapak
554-
// Licensed under the MIT License.
555-
func goldenEqual(img1, img2 *image.NRGBA) bool {
556-
maxDiff := 0
557-
if usesFMA {
558-
maxDiff = 1
559-
}
560-
if !img1.Rect.Eq(img2.Rect) {
561-
return false
562-
}
563-
if len(img1.Pix) != len(img2.Pix) {
564-
return false
565-
}
566-
for i := 0; i < len(img1.Pix); i++ {
567-
diff := int(img1.Pix[i]) - int(img2.Pix[i])
568-
if diff < 0 {
569-
diff = -diff
570-
}
571-
if diff > maxDiff {
572-
return false
573-
}
574-
}
575-
return true
576-
}
577-
578-
// Issue #8729
579-
func TestImageOperationsGoldenWebp(t *testing.T) {
580-
if !htesting.IsCI() {
581-
t.Skip("skip long running test in local mode")
582-
}
583-
if !webp.Supports() {
584-
t.Skip("skip webp test")
585-
}
586-
c := qt.New(t)
587-
c.Parallel()
588-
589-
devMode := false
590-
591-
testImages := []string{"fuzzy-cirlcle.png"}
592-
593-
spec, workDir := newTestResourceOsFs(c)
594-
defer func() {
595-
if !devMode {
596-
os.Remove(workDir)
597-
}
598-
}()
599-
600-
if devMode {
601-
fmt.Println(workDir)
602-
}
603-
604-
for _, imageName := range testImages {
605-
image := fetchImageForSpec(spec, c, imageName)
606-
imageWebp, err := image.Resize("200x webp")
607-
c.Assert(err, qt.IsNil)
608-
c.Assert(imageWebp.Width(), qt.Equals, 200)
609-
}
610-
611-
if devMode {
612-
return
613-
}
614-
615-
dir1 := filepath.Join(workDir, "resources/_gen/images/a")
616-
dir2 := filepath.FromSlash("testdata/golden_webp")
617-
618-
assetGoldenDirs(c, dir1, dir2)
619-
}
620-
621-
func TestImageOperationsGolden(t *testing.T) {
622-
if !htesting.IsCI() {
623-
t.Skip("skip long running test in local mode")
624-
}
625-
c := qt.New(t)
626-
c.Parallel()
627-
628-
// Note, if you're enabling this on a MacOS M1 (ARM) you need to run the test with GOARCH=amd64.
629-
// GOARCH=amd64 go test -count 1 -timeout 30s -run "^TestImageOperationsGolden$" ./resources -v
630-
// The above will print out a folder.
631-
// Replace testdata/golden with resources/_gen/images in that folder.
632-
devMode := false
633-
634-
testImages := []string{"sunset.jpg", "gohugoio8.png", "gohugoio24.png"}
635-
636-
spec, workDir := newTestResourceOsFs(c)
637-
defer func() {
638-
if !devMode {
639-
os.Remove(workDir)
640-
}
641-
}()
642-
643-
if devMode {
644-
fmt.Println(workDir)
645-
}
646-
647-
gopher := fetchImageForSpec(spec, c, "gopher-hero8.png")
648-
var err error
649-
gopher, err = gopher.Resize("30x")
650-
c.Assert(err, qt.IsNil)
651-
652-
f := &images.Filters{}
653-
654-
sunset := fetchImageForSpec(spec, c, "sunset.jpg")
655-
656-
// Test PNGs with alpha channel.
657-
for _, img := range []string{"gopher-hero8.png", "gradient-circle.png"} {
658-
orig := fetchImageForSpec(spec, c, img)
659-
for _, resizeSpec := range []string{"200x #e3e615", "200x jpg #e3e615"} {
660-
resized, err := orig.Resize(resizeSpec)
661-
c.Assert(err, qt.IsNil)
662-
rel := resized.RelPermalink()
663-
664-
c.Assert(rel, qt.Not(qt.Equals), "")
665-
666-
}
667-
668-
// Check the Opacity filter.
669-
opacity30, err := orig.Filter(f.Opacity(30))
670-
c.Assert(err, qt.IsNil)
671-
overlay, err := sunset.Filter(f.Overlay(opacity30.(images.ImageSource), 20, 20))
672-
c.Assert(err, qt.IsNil)
673-
rel := overlay.RelPermalink()
674-
c.Assert(rel, qt.Not(qt.Equals), "")
675-
676-
}
677-
678-
// A simple Gif file (no animation).
679-
orig := fetchImageForSpec(spec, c, "gohugoio-card.gif")
680-
for _, width := range []int{100, 220} {
681-
resized, err := orig.Resize(fmt.Sprintf("%dx", width))
682-
c.Assert(err, qt.IsNil)
683-
rel := resized.RelPermalink()
684-
c.Assert(rel, qt.Not(qt.Equals), "")
685-
c.Assert(resized.Width(), qt.Equals, width)
686-
}
687-
688-
// Animated GIF
689-
orig = fetchImageForSpec(spec, c, "giphy.gif")
690-
for _, resizeSpec := range []string{"200x", "512x", "100x jpg"} {
691-
resized, err := orig.Resize(resizeSpec)
692-
c.Assert(err, qt.IsNil)
693-
rel := resized.RelPermalink()
694-
c.Assert(rel, qt.Not(qt.Equals), "")
695-
}
696-
697-
for _, img := range testImages {
698-
699-
orig := fetchImageForSpec(spec, c, img)
700-
for _, resizeSpec := range []string{"200x100", "600x", "200x r90 q50 Box"} {
701-
resized, err := orig.Resize(resizeSpec)
702-
c.Assert(err, qt.IsNil)
703-
rel := resized.RelPermalink()
704-
c.Assert(rel, qt.Not(qt.Equals), "")
705-
}
706-
707-
for _, fillSpec := range []string{"300x200 Gaussian Smart", "100x100 Center", "300x100 TopLeft NearestNeighbor", "400x200 BottomLeft"} {
708-
resized, err := orig.Fill(fillSpec)
709-
c.Assert(err, qt.IsNil)
710-
rel := resized.RelPermalink()
711-
c.Assert(rel, qt.Not(qt.Equals), "")
712-
}
713-
714-
for _, fitSpec := range []string{"300x200 Linear"} {
715-
resized, err := orig.Fit(fitSpec)
716-
c.Assert(err, qt.IsNil)
717-
rel := resized.RelPermalink()
718-
c.Assert(rel, qt.Not(qt.Equals), "")
719-
}
720-
721-
filters := []gift.Filter{
722-
f.Grayscale(),
723-
f.GaussianBlur(6),
724-
f.Saturation(50),
725-
f.Sepia(100),
726-
f.Brightness(30),
727-
f.ColorBalance(10, -10, -10),
728-
f.Colorize(240, 50, 100),
729-
f.Gamma(1.5),
730-
f.UnsharpMask(1, 1, 0),
731-
f.Sigmoid(0.5, 7),
732-
f.Pixelate(5),
733-
f.Invert(),
734-
f.Hue(22),
735-
f.Contrast(32.5),
736-
f.Overlay(gopher.(images.ImageSource), 20, 30),
737-
f.Text("No options"),
738-
f.Text("This long text is to test line breaks. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."),
739-
f.Text("Hugo rocks!", map[string]any{"x": 3, "y": 3, "size": 20, "color": "#fc03b1"}),
740-
}
741-
742-
resized, err := orig.Fill("400x200 center")
743-
c.Assert(err, qt.IsNil)
744-
745-
for _, filter := range filters {
746-
resized, err := resized.Filter(filter)
747-
c.Assert(err, qt.IsNil)
748-
rel := resized.RelPermalink()
749-
c.Assert(rel, qt.Not(qt.Equals), "")
750-
}
751-
752-
resized, err = resized.Filter(filters[0:4])
753-
c.Assert(err, qt.IsNil)
754-
rel := resized.RelPermalink()
755-
c.Assert(rel, qt.Not(qt.Equals), "")
756-
}
757-
758-
if devMode {
759-
return
760-
}
761-
762-
dir1 := filepath.Join(workDir, "resources/_gen/images/a/")
763-
dir2 := filepath.FromSlash("testdata/golden")
764-
765-
assetGoldenDirs(c, dir1, dir2)
766-
}
767-
768-
func assetGoldenDirs(c *qt.C, dir1, dir2 string) {
769-
// The two dirs above should now be the same.
770-
dirinfos1, err := os.ReadDir(dir1)
771-
c.Assert(err, qt.IsNil)
772-
dirinfos2, err := os.ReadDir(dir2)
773-
c.Assert(err, qt.IsNil)
774-
c.Assert(len(dirinfos1), qt.Equals, len(dirinfos2))
775-
776-
for i, fi1 := range dirinfos1 {
777-
fi2 := dirinfos2[i]
778-
c.Assert(fi1.Name(), qt.Equals, fi2.Name(), qt.Commentf("i=%d", i))
779-
780-
f1, err := os.Open(filepath.Join(dir1, fi1.Name()))
781-
c.Assert(err, qt.IsNil)
782-
f2, err := os.Open(filepath.Join(dir2, fi2.Name()))
783-
c.Assert(err, qt.IsNil)
784-
785-
decodeAll := func(f *os.File) []image.Image {
786-
var images []image.Image
787-
788-
if strings.HasSuffix(f.Name(), ".gif") {
789-
gif, err := gif.DecodeAll(f)
790-
c.Assert(err, qt.IsNil)
791-
images = make([]image.Image, len(gif.Image))
792-
for i, img := range gif.Image {
793-
images[i] = img
794-
}
795-
} else {
796-
img, _, err := image.Decode(f)
797-
c.Assert(err, qt.IsNil)
798-
images = append(images, img)
799-
}
800-
return images
801-
}
802-
803-
imgs1 := decodeAll(f1)
804-
imgs2 := decodeAll(f2)
805-
c.Assert(len(imgs1), qt.Equals, len(imgs2))
806-
807-
LOOP:
808-
for i, img1 := range imgs1 {
809-
img2 := imgs2[i]
810-
nrgba1 := image.NewNRGBA(img1.Bounds())
811-
gift.New().Draw(nrgba1, img1)
812-
nrgba2 := image.NewNRGBA(img2.Bounds())
813-
gift.New().Draw(nrgba2, img2)
814-
815-
if !goldenEqual(nrgba1, nrgba2) {
816-
switch fi1.Name() {
817-
case "giphy_hu13007323561585908901.gif",
818-
"gohugoio8_hu12690451569630232821.png",
819-
"gohugoio8_hu1619987041333606118.png",
820-
"gohugoio8_hu18164141965527013334.png":
821-
c.Log("expectedly differs from golden due to dithering:", fi1.Name())
822-
default:
823-
c.Errorf("resulting image differs from golden: %s", fi1.Name())
824-
break LOOP
825-
}
826-
}
827-
}
828-
829-
if !usesFMA {
830-
c.Assert(fi1, eq, fi2)
831-
832-
_, err = f1.Seek(0, 0)
833-
c.Assert(err, qt.IsNil)
834-
_, err = f2.Seek(0, 0)
835-
c.Assert(err, qt.IsNil)
836-
837-
hash1, _, err := hashing.XXHashFromReader(f1)
838-
c.Assert(err, qt.IsNil)
839-
hash2, _, err := hashing.XXHashFromReader(f2)
840-
c.Assert(err, qt.IsNil)
841-
842-
c.Assert(hash1, qt.Equals, hash2)
843-
}
844-
845-
f1.Close()
846-
f2.Close()
847-
}
848-
}
849-
850526
func BenchmarkResizeParallel(b *testing.B) {
851527
c := qt.New(b)
852528
_, img := fetchSunset(c)

0 commit comments

Comments
 (0)