resources/images: Refactor golden image tests to locate them closer to the implementation

This commit is contained in:
Bjørn Erik Pedersen 2025-01-11 14:10:30 +02:00
parent 06cc8673fa
commit 2501de7aab
59 changed files with 307 additions and 241 deletions

View file

@ -14,63 +14,22 @@
package images_test
import (
"image"
"image/gif"
_ "image/jpeg"
"io/fs"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/disintegration/gift"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/hashing"
"github.com/gohugoio/hugo/common/hugio"
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugolib"
"github.com/google/go-cmp/cmp"
"github.com/gohugoio/hugo/resources/images/imagetesting"
)
var eq = qt.CmpEquals(
cmp.Comparer(func(p1, p2 os.FileInfo) bool {
return p1.Name() == p2.Name() && p1.Size() == p2.Size() && p1.IsDir() == p2.IsDir()
}),
cmp.Comparer(func(d1, d2 fs.DirEntry) bool {
p1, err1 := d1.Info()
p2, err2 := d2.Info()
if err1 != nil || err2 != nil {
return false
}
return p1.Name() == p2.Name() && p1.Size() == p2.Size() && p1.IsDir() == p2.IsDir()
}),
)
var goldenOpts = struct {
// Toggle this to write golden files to disk.
// Note: Remember to set this to false before committing.
writeGoldenFiles bool
// This will skip any assertions. Useful when adding new golden variants to a test.
devMode bool
}{
writeGoldenFiles: false,
devMode: false,
}
// Note, if you're enabling writeGoldenFiles on a MacOS ARM 64 you need to run the test with GOARCH=amd64, e.g.
// GOARCH=amd64 go test -count 1 -timeout 30s -run "^TestGolden" ./resources/images
func TestGoldenFiltersMisc(t *testing.T) {
func TestImagesGoldenFiltersMisc(t *testing.T) {
t.Parallel()
if skipGolden {
if imagetesting.SkipGoldenTests {
t.Skip("Skip golden test on this architecture")
}
// Will be used to generate golden files.
name := "filters_misc"
// Will be used as the base folder for generated images.
name := "filters/misc"
files := `
-- hugo.toml --
@ -82,9 +41,9 @@ sourcefilename: ../testdata/sunset.jpg
sourcefilename: ../testdata/gopher-hero8.png
-- layouts/index.html --
Home.
{{ $sunset := resources.Get "sunset.jpg" }}
{{ $sunset := (resources.Get "sunset.jpg").Resize "x300" }}
{{ $sunsetGrayscale := $sunset.Filter (images.Grayscale) }}
{{ $gopher := resources.Get "gopher.png" }}
{{ $gopher := (resources.Get "gopher.png").Resize "x80" }}
{{ $overlayFilter := images.Overlay $gopher 20 20 }}
{{ $textOpts := dict
@ -130,18 +89,23 @@ Home.
{{ end }}
`
runGolden(t, name, files)
opts := imagetesting.DefaultGoldenOpts
opts.T = t
opts.Name = name
opts.Files = files
imagetesting.RunGolden(opts)
}
func TestGoldenFiltersMask(t *testing.T) {
func TestImagesGoldenFiltersMask(t *testing.T) {
t.Parallel()
if skipGolden {
if imagetesting.SkipGoldenTests {
t.Skip("Skip golden test on this architecture")
}
// Will be used to generate golden files.
name := "filters_mask"
// Will be used as the base folder for generated images.
name := "filters/mask"
files := `
-- hugo.toml --
@ -163,7 +127,12 @@ Home.
{{ template "mask" (dict "name" "transparant.png" "base" $sunset "mask" $mask) }}
{{ template "mask" (dict "name" "yellow.jpg" "base" $sunset "mask" $mask) }}
{{ template "mask" (dict "name" "wide.jpg" "base" $sunset "mask" $mask "spec" "resize 600x200") }}
{{/* This looks a little odd, but is correct and the recommended way to do this.
This will 1. Scale the image to x300, 2. Apply the mask, 3. Create the final image with background color #323ea.
It's possible to have multiple images.Process filters in the chain, but for the options for the final image (target format, bgGolor etc.),
the last entry will win.
*/}}
{{ template "mask" (dict "name" "blue.jpg" "base" $sunset "mask" $mask "spec" "resize x300 #323ea8") }}
{{ define "mask"}}
{{ $ext := path.Ext .name }}
@ -171,7 +140,7 @@ Home.
{{ errorf "No extension in %q" .name }}
{{ end }}
{{ $format := strings.TrimPrefix "." $ext }}
{{ $spec := .spec | default (printf "resize 300x300 %s" $format) }}
{{ $spec := .spec | default (printf "resize x300 %s" $format) }}
{{ $filters := slice (images.Process $spec) (images.Mask .mask) }}
{{ $name := printf "images/%s" .name }}
{{ $img := .base.Filter $filters }}
@ -181,18 +150,23 @@ Home.
{{ end }}
`
runGolden(t, name, files)
opts := imagetesting.DefaultGoldenOpts
opts.T = t
opts.Name = name
opts.Files = files
imagetesting.RunGolden(opts)
}
func TestGoldenFiltersText(t *testing.T) {
func TestImagesGoldenFiltersText(t *testing.T) {
t.Parallel()
if skipGolden {
if imagetesting.SkipGoldenTests {
t.Skip("Skip golden test on this architecture")
}
// Will be used to generate golden files.
name := "filters_text"
// Will be used as the base folder for generated images.
name := "filters/text"
files := `
-- hugo.toml --
@ -230,18 +204,23 @@ Home.
{{ end }}
`
runGolden(t, name, files)
opts := imagetesting.DefaultGoldenOpts
opts.T = t
opts.Name = name
opts.Files = files
imagetesting.RunGolden(opts)
}
func TestGoldenProcessMisc(t *testing.T) {
func TestImagesGoldenProcessMisc(t *testing.T) {
t.Parallel()
if skipGolden {
if imagetesting.SkipGoldenTests {
t.Skip("Skip golden test on this architecture")
}
// Will be used to generate golden files.
name := "process_misc"
// Will be used as the base folder for generated images.
name := "process/misc"
files := `
-- hugo.toml --
@ -277,180 +256,10 @@ Home.
{{ end }}
`
runGolden(t, name, files)
opts := imagetesting.DefaultGoldenOpts
opts.T = t
opts.Name = name
opts.Files = files
imagetesting.RunGolden(opts)
}
func TestGoldenFuncs(t *testing.T) {
t.Parallel()
if skipGolden {
t.Skip("Skip golden test on this architecture")
}
// Will be used to generate golden files.
name := "funcs"
files := `
-- hugo.toml --
-- assets/sunset.jpg --
sourcefilename: ../testdata/sunset.jpg
-- layouts/index.html --
Home.
{{ template "copy" (dict "name" "qr-default.png" "img" (images.QR "https://gohugo.io")) }}
{{ template "copy" (dict "name" "qr-level-high_scale-6.png" "img" (images.QR "https://gohugo.io" (dict "level" "high" "scale" 6))) }}
{{ define "copy"}}
{{ if lt (len (path.Ext .name)) 4 }}
{{ errorf "No extension in %q" .name }}
{{ end }}
{{ $img := .img }}
{{ $name := printf "images/%s" .name }}
{{ with $img | resources.Copy $name }}
{{ .Publish }}
{{ end }}
{{ end }}
`
runGolden(t, name, files)
}
func runGolden(t testing.TB, name, files string) *hugolib.IntegrationTestBuilder {
t.Helper()
c := hugolib.Test(t, files, hugolib.TestOptWithOSFs()) // hugolib.TestOptWithPrintAndKeepTempDir(true))
c.AssertFileContent("public/index.html", "Home.")
outputDir := filepath.Join(c.H.Conf.WorkingDir(), "public", "images")
goldenBaseDir := filepath.Join("testdata", "images_golden")
goldenDir := filepath.Join(goldenBaseDir, name)
if goldenOpts.writeGoldenFiles {
c.Assert(htesting.IsRealCI(), qt.IsFalse)
c.Assert(os.MkdirAll(goldenBaseDir, 0o777), qt.IsNil)
c.Assert(os.RemoveAll(goldenDir), qt.IsNil)
c.Assert(hugio.CopyDir(hugofs.Os, outputDir, goldenDir, nil), qt.IsNil)
return c
}
if goldenOpts.devMode {
c.Assert(htesting.IsRealCI(), qt.IsFalse)
return c
}
decodeAll := func(f *os.File) []image.Image {
c.Helper()
var images []image.Image
if strings.HasSuffix(f.Name(), ".gif") {
gif, err := gif.DecodeAll(f)
c.Assert(err, qt.IsNil, qt.Commentf(f.Name()))
images = make([]image.Image, len(gif.Image))
for i, img := range gif.Image {
images[i] = img
}
} else {
img, _, err := image.Decode(f)
c.Assert(err, qt.IsNil, qt.Commentf(f.Name()))
images = append(images, img)
}
return images
}
entries1, err := os.ReadDir(outputDir)
c.Assert(err, qt.IsNil)
entries2, err := os.ReadDir(goldenDir)
c.Assert(err, qt.IsNil)
c.Assert(len(entries1), qt.Equals, len(entries2))
for i, e1 := range entries1 {
c.Assert(filepath.Ext(e1.Name()), qt.Not(qt.Equals), "")
func() {
e2 := entries2[i]
f1, err := os.Open(filepath.Join(outputDir, e1.Name()))
c.Assert(err, qt.IsNil)
defer f1.Close()
f2, err := os.Open(filepath.Join(goldenDir, e2.Name()))
c.Assert(err, qt.IsNil)
defer f2.Close()
imgs2 := decodeAll(f2)
imgs1 := decodeAll(f1)
c.Assert(len(imgs1), qt.Equals, len(imgs2))
if !usesFMA {
c.Assert(e1, eq, e2)
_, err = f1.Seek(0, 0)
c.Assert(err, qt.IsNil)
_, err = f2.Seek(0, 0)
c.Assert(err, qt.IsNil)
hash1, _, err := hashing.XXHashFromReader(f1)
c.Assert(err, qt.IsNil)
hash2, _, err := hashing.XXHashFromReader(f2)
c.Assert(err, qt.IsNil)
c.Assert(hash1, qt.Equals, hash2)
}
for i, img1 := range imgs1 {
img2 := imgs2[i]
nrgba1 := image.NewNRGBA(img1.Bounds())
gift.New().Draw(nrgba1, img1)
nrgba2 := image.NewNRGBA(img2.Bounds())
gift.New().Draw(nrgba2, img2)
c.Assert(goldenEqual(nrgba1, nrgba2), qt.Equals, true, qt.Commentf(e1.Name()))
}
}()
}
return c
}
// goldenEqual compares two NRGBA images. It is used in golden tests only.
// A small tolerance is allowed on architectures using "fused multiply and add"
// (FMA) instruction to accommodate for floating-point rounding differences
// with control golden images that were generated on amd64 architecture.
// See https://golang.org/ref/spec#Floating_point_operators
// and https://github.com/gohugoio/hugo/issues/6387 for more information.
//
// Based on https://github.com/disintegration/gift/blob/a999ff8d5226e5ab14b64a94fca07c4ac3f357cf/gift_test.go#L598-L625
// Copyright (c) 2014-2019 Grigory Dryapak
// Licensed under the MIT License.
func goldenEqual(img1, img2 *image.NRGBA) bool {
maxDiff := 0
if runtime.GOARCH != "amd64" {
// The golden files are created using the AMD64 architecture.
// Be lenient on other platforms due to floaging point and dithering differences.
maxDiff = 15
}
if !img1.Rect.Eq(img2.Rect) {
return false
}
if len(img1.Pix) != len(img2.Pix) {
return false
}
for i := 0; i < len(img1.Pix); i++ {
diff := int(img1.Pix[i]) - int(img2.Pix[i])
if diff < 0 {
diff = -diff
}
if diff > maxDiff {
return false
}
}
return true
}
// We don't have a CI test environment for these, and there are known dithering issues that makes these time consuming to maintain.
var skipGolden = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x"
// usesFMA indicates whether "fused multiply and add" (FMA) instruction is
// used. The command "grep FMADD go/test/codegen/floats.go" can help keep
// the FMA-using architecture list updated.
var usesFMA = runtime.GOARCH == "s390x" ||
runtime.GOARCH == "ppc64" ||
runtime.GOARCH == "ppc64le" ||
runtime.GOARCH == "arm64" ||
runtime.GOARCH == "riscv64"

View file

@ -0,0 +1,215 @@
// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package imagetesting
import (
"image"
"image/gif"
"io/fs"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
qt "github.com/frankban/quicktest"
"github.com/google/go-cmp/cmp"
"github.com/disintegration/gift"
"github.com/gohugoio/hugo/common/hashing"
"github.com/gohugoio/hugo/common/hugio"
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugolib"
)
var eq = qt.CmpEquals(
cmp.Comparer(func(p1, p2 os.FileInfo) bool {
return p1.Name() == p2.Name() && p1.Size() == p2.Size() && p1.IsDir() == p2.IsDir()
}),
cmp.Comparer(func(d1, d2 fs.DirEntry) bool {
p1, err1 := d1.Info()
p2, err2 := d2.Info()
if err1 != nil || err2 != nil {
return false
}
return p1.Name() == p2.Name() && p1.Size() == p2.Size() && p1.IsDir() == p2.IsDir()
}),
)
// GoldenImageTestOpts provides options for a golden image test.
type GoldenImageTestOpts struct {
// The test.
T testing.TB
// Name of the test. Will be used as the base folder for generated images.
// Slashes allowed and encouraged.
Name string
// The test site's files in txttar format.
Files string
// Set to true to write golden files to disk.
WriteFiles bool
// Set to true to skip any assertions. Useful when adding new golden variants to a test.
DevMode bool
}
// To rebuild all Golden image tests, toggle WriteFiles=true and run:
// GOARCH=amd64 go test -count 1 -timeout 30s -run "^TestImagesGolden" ./...
// TODO(bep) see if we can do this via flags.
var DefaultGoldenOpts = GoldenImageTestOpts{
WriteFiles: false,
DevMode: false,
}
func RunGolden(opts GoldenImageTestOpts) *hugolib.IntegrationTestBuilder {
opts.T.Helper()
c := hugolib.Test(opts.T, opts.Files, hugolib.TestOptWithOSFs()) // hugolib.TestOptWithPrintAndKeepTempDir(true))
c.AssertFileContent("public/index.html", "Home.")
outputDir := filepath.Join(c.H.Conf.WorkingDir(), "public", "images")
goldenBaseDir := filepath.Join("testdata", "images_golden")
goldenDir := filepath.Join(goldenBaseDir, filepath.FromSlash(opts.Name))
if opts.WriteFiles {
c.Assert(htesting.IsRealCI(), qt.IsFalse)
c.Assert(os.MkdirAll(goldenBaseDir, 0o777), qt.IsNil)
c.Assert(os.RemoveAll(goldenDir), qt.IsNil)
c.Assert(hugio.CopyDir(hugofs.Os, outputDir, goldenDir, nil), qt.IsNil)
return c
}
if opts.DevMode {
c.Assert(htesting.IsRealCI(), qt.IsFalse)
return c
}
decodeAll := func(f *os.File) []image.Image {
c.Helper()
var images []image.Image
if strings.HasSuffix(f.Name(), ".gif") {
gif, err := gif.DecodeAll(f)
c.Assert(err, qt.IsNil, qt.Commentf(f.Name()))
images = make([]image.Image, len(gif.Image))
for i, img := range gif.Image {
images[i] = img
}
} else {
img, _, err := image.Decode(f)
c.Assert(err, qt.IsNil, qt.Commentf(f.Name()))
images = append(images, img)
}
return images
}
entries1, err := os.ReadDir(outputDir)
c.Assert(err, qt.IsNil)
entries2, err := os.ReadDir(goldenDir)
c.Assert(err, qt.IsNil)
c.Assert(len(entries1), qt.Equals, len(entries2))
for i, e1 := range entries1 {
c.Assert(filepath.Ext(e1.Name()), qt.Not(qt.Equals), "")
func() {
e2 := entries2[i]
f1, err := os.Open(filepath.Join(outputDir, e1.Name()))
c.Assert(err, qt.IsNil)
defer f1.Close()
f2, err := os.Open(filepath.Join(goldenDir, e2.Name()))
c.Assert(err, qt.IsNil)
defer f2.Close()
imgs2 := decodeAll(f2)
imgs1 := decodeAll(f1)
c.Assert(len(imgs1), qt.Equals, len(imgs2))
if !UsesFMA {
c.Assert(e1, eq, e2)
_, err = f1.Seek(0, 0)
c.Assert(err, qt.IsNil)
_, err = f2.Seek(0, 0)
c.Assert(err, qt.IsNil)
hash1, _, err := hashing.XXHashFromReader(f1)
c.Assert(err, qt.IsNil)
hash2, _, err := hashing.XXHashFromReader(f2)
c.Assert(err, qt.IsNil)
c.Assert(hash1, qt.Equals, hash2)
}
for i, img1 := range imgs1 {
img2 := imgs2[i]
nrgba1 := image.NewNRGBA(img1.Bounds())
gift.New().Draw(nrgba1, img1)
nrgba2 := image.NewNRGBA(img2.Bounds())
gift.New().Draw(nrgba2, img2)
c.Assert(goldenEqual(nrgba1, nrgba2), qt.Equals, true, qt.Commentf(e1.Name()))
}
}()
}
return c
}
// goldenEqual compares two NRGBA images. It is used in golden tests only.
// A small tolerance is allowed on architectures using "fused multiply and add"
// (FMA) instruction to accommodate for floating-point rounding differences
// with control golden images that were generated on amd64 architecture.
// See https://golang.org/ref/spec#Floating_point_operators
// and https://github.com/gohugoio/hugo/issues/6387 for more information.
//
// Based on https://github.com/disintegration/gift/blob/a999ff8d5226e5ab14b64a94fca07c4ac3f357cf/gift_test.go#L598-L625
// Copyright (c) 2014-2019 Grigory Dryapak
// Licensed under the MIT License.
func goldenEqual(img1, img2 *image.NRGBA) bool {
maxDiff := 0
if runtime.GOARCH != "amd64" {
// The golden files are created using the AMD64 architecture.
// Be lenient on other platforms due to floaging point and dithering differences.
maxDiff = 15
}
if !img1.Rect.Eq(img2.Rect) {
return false
}
if len(img1.Pix) != len(img2.Pix) {
return false
}
for i := 0; i < len(img1.Pix); i++ {
diff := int(img1.Pix[i]) - int(img2.Pix[i])
if diff < 0 {
diff = -diff
}
if diff > maxDiff {
return false
}
}
return true
}
// We don't have a CI test environment for these, and there are known dithering issues that makes these time consuming to maintain.
var SkipGoldenTests = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x"
// UsesFMA indicates whether "fused multiply and add" (FMA) instruction is
// used. The command "grep FMADD go/test/codegen/floats.go" can help keep
// the FMA-using architecture list updated.
var UsesFMA = runtime.GOARCH == "s390x" ||
runtime.GOARCH == "ppc64" ||
runtime.GOARCH == "ppc64le" ||
runtime.GOARCH == "arm64" ||
runtime.GOARCH == "riscv64"

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

View file

Before

Width:  |  Height:  |  Size: 8 KiB

After

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View file

Before

Width:  |  Height:  |  Size: 688 B

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View file

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View file

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

View file

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View file

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -19,6 +19,7 @@ import (
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/resources/images/imagetesting"
)
func TestImageConfigFromModule(t *testing.T) {
@ -100,3 +101,44 @@ disableKinds = ['page','rss','section','sitemap','taxonomy','term']
b, err = hugolib.TestE(t, files)
b.Assert(err.Error(), qt.Contains, "cannot encode an empty string")
}
func TestImagesGoldenFuncs(t *testing.T) {
t.Parallel()
if imagetesting.SkipGoldenTests {
t.Skip("Skip golden test on this architecture")
}
// Will be used as the base folder for generated images.
name := "funcs"
files := `
-- hugo.toml --
-- assets/sunset.jpg --
sourcefilename: ../../resources/testdata/sunset.jpg
-- layouts/index.html --
Home.
{{ template "copy" (dict "name" "qr-default.png" "img" (images.QR "https://gohugo.io")) }}
{{ template "copy" (dict "name" "qr-level-high_scale-6.png" "img" (images.QR "https://gohugo.io" (dict "level" "high" "scale" 6))) }}
{{ define "copy"}}
{{ if lt (len (path.Ext .name)) 4 }}
{{ errorf "No extension in %q" .name }}
{{ end }}
{{ $img := .img }}
{{ $name := printf "images/%s" .name }}
{{ with $img | resources.Copy $name }}
{{ .Publish }}
{{ end }}
{{ end }}
`
opts := imagetesting.DefaultGoldenOpts
opts.T = t
opts.Name = name
opts.Files = files
imagetesting.RunGolden(opts)
}

View file

Before

Width:  |  Height:  |  Size: 663 B

After

Width:  |  Height:  |  Size: 663 B

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB