github.com/pietrocarrara/hugo@v0.47.1/resource/image_test.go (about)

     1  // Copyright 2018 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package resource
    15  
    16  import (
    17  	"fmt"
    18  	"math/rand"
    19  	"path/filepath"
    20  	"strconv"
    21  	"testing"
    22  
    23  	"github.com/disintegration/imaging"
    24  
    25  	"sync"
    26  
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func TestParseImageConfig(t *testing.T) {
    31  	for i, this := range []struct {
    32  		in     string
    33  		expect interface{}
    34  	}{
    35  		{"300x400", newImageConfig(300, 400, 0, 0, "", "")},
    36  		{"100x200 bottomRight", newImageConfig(100, 200, 0, 0, "", "BottomRight")},
    37  		{"10x20 topleft Lanczos", newImageConfig(10, 20, 0, 0, "Lanczos", "topleft")},
    38  		{"linear left 10x r180", newImageConfig(10, 0, 0, 180, "linear", "left")},
    39  		{"x20 riGht Cosine q95", newImageConfig(0, 20, 95, 0, "cosine", "right")},
    40  
    41  		{"", false},
    42  		{"foo", false},
    43  	} {
    44  		result, err := parseImageConfig(this.in)
    45  		if b, ok := this.expect.(bool); ok && !b {
    46  			if err == nil {
    47  				t.Errorf("[%d] parseImageConfig didn't return an expected error", i)
    48  			}
    49  		} else {
    50  			if err != nil {
    51  				t.Fatalf("[%d] err: %s", i, err)
    52  			}
    53  			if fmt.Sprint(result) != fmt.Sprint(this.expect) {
    54  				t.Fatalf("[%d] got\n%v\n but expected\n%v", i, result, this.expect)
    55  			}
    56  		}
    57  	}
    58  }
    59  
    60  func TestImageTransformBasic(t *testing.T) {
    61  
    62  	assert := require.New(t)
    63  
    64  	image := fetchSunset(assert)
    65  
    66  	assert.Equal("/a/sunset.jpg", image.RelPermalink())
    67  	assert.Equal("image", image.ResourceType())
    68  
    69  	resized, err := image.Resize("300x200")
    70  	assert.NoError(err)
    71  	assert.True(image != resized)
    72  	assert.True(image.genericResource != resized.genericResource)
    73  	assert.True(image.sourceFilename != resized.sourceFilename)
    74  
    75  	resized0x, err := image.Resize("x200")
    76  	assert.NoError(err)
    77  	assert.Equal(320, resized0x.Width())
    78  	assert.Equal(200, resized0x.Height())
    79  	assertFileCache(assert, image.spec.BaseFs.Resources.Fs, resized0x.RelPermalink(), 320, 200)
    80  
    81  	resizedx0, err := image.Resize("200x")
    82  	assert.NoError(err)
    83  	assert.Equal(200, resizedx0.Width())
    84  	assert.Equal(125, resizedx0.Height())
    85  	assertFileCache(assert, image.spec.BaseFs.Resources.Fs, resizedx0.RelPermalink(), 200, 125)
    86  
    87  	resizedAndRotated, err := image.Resize("x200 r90")
    88  	assert.NoError(err)
    89  	assert.Equal(125, resizedAndRotated.Width())
    90  	assert.Equal(200, resizedAndRotated.Height())
    91  	assertFileCache(assert, image.spec.BaseFs.Resources.Fs, resizedAndRotated.RelPermalink(), 125, 200)
    92  
    93  	assert.Equal("/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_300x200_resize_q68_linear.jpg", resized.RelPermalink())
    94  	assert.Equal(300, resized.Width())
    95  	assert.Equal(200, resized.Height())
    96  
    97  	fitted, err := resized.Fit("50x50")
    98  	assert.NoError(err)
    99  	assert.Equal("/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_625708021e2bb281c9f1002f88e4753f.jpg", fitted.RelPermalink())
   100  	assert.Equal(50, fitted.Width())
   101  	assert.Equal(33, fitted.Height())
   102  
   103  	// Check the MD5 key threshold
   104  	fittedAgain, _ := fitted.Fit("10x20")
   105  	fittedAgain, err = fittedAgain.Fit("10x20")
   106  	assert.NoError(err)
   107  	assert.Equal("/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_3f65ba24dc2b7fba0f56d7f104519157.jpg", fittedAgain.RelPermalink())
   108  	assert.Equal(10, fittedAgain.Width())
   109  	assert.Equal(6, fittedAgain.Height())
   110  
   111  	filled, err := image.Fill("200x100 bottomLeft")
   112  	assert.NoError(err)
   113  	assert.Equal("/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x100_fill_q68_linear_bottomleft.jpg", filled.RelPermalink())
   114  	assert.Equal(200, filled.Width())
   115  	assert.Equal(100, filled.Height())
   116  	assertFileCache(assert, image.spec.BaseFs.Resources.Fs, filled.RelPermalink(), 200, 100)
   117  
   118  	smart, err := image.Fill("200x100 smart")
   119  	assert.NoError(err)
   120  	assert.Equal(fmt.Sprintf("/a/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x100_fill_q68_linear_smart%d.jpg", smartCropVersionNumber), smart.RelPermalink())
   121  	assert.Equal(200, smart.Width())
   122  	assert.Equal(100, smart.Height())
   123  	assertFileCache(assert, image.spec.BaseFs.Resources.Fs, smart.RelPermalink(), 200, 100)
   124  
   125  	// Check cache
   126  	filledAgain, err := image.Fill("200x100 bottomLeft")
   127  	assert.NoError(err)
   128  	assert.True(filled == filledAgain)
   129  	assert.True(filled.sourceFilename == filledAgain.sourceFilename)
   130  	assertFileCache(assert, image.spec.BaseFs.Resources.Fs, filledAgain.RelPermalink(), 200, 100)
   131  
   132  }
   133  
   134  // https://github.com/gohugoio/hugo/issues/4261
   135  func TestImageTransformLongFilename(t *testing.T) {
   136  	assert := require.New(t)
   137  
   138  	image := fetchImage(assert, "1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph.jpg")
   139  	assert.NotNil(image)
   140  
   141  	resized, err := image.Resize("200x")
   142  	assert.NoError(err)
   143  	assert.NotNil(resized)
   144  	assert.Equal(200, resized.Width())
   145  	assert.Equal("/a/_hu59e56ffff1bc1d8d122b1403d34e039f_90587_65b757a6e14debeae720fe8831f0a9bc.jpg", resized.RelPermalink())
   146  	resized, err = resized.Resize("100x")
   147  	assert.NoError(err)
   148  	assert.NotNil(resized)
   149  	assert.Equal(100, resized.Width())
   150  	assert.Equal("/a/_hu59e56ffff1bc1d8d122b1403d34e039f_90587_c876768085288f41211f768147ba2647.jpg", resized.RelPermalink())
   151  }
   152  
   153  func TestImageTransformConcurrent(t *testing.T) {
   154  
   155  	var wg sync.WaitGroup
   156  
   157  	assert := require.New(t)
   158  
   159  	spec := newTestResourceOsFs(assert)
   160  
   161  	image := fetchImageForSpec(spec, assert, "sunset.jpg")
   162  
   163  	for i := 0; i < 4; i++ {
   164  		wg.Add(1)
   165  		go func(id int) {
   166  			defer wg.Done()
   167  			for j := 0; j < 5; j++ {
   168  				img := image
   169  				for k := 0; k < 2; k++ {
   170  					r1, err := img.Resize(fmt.Sprintf("%dx", id-k))
   171  					if err != nil {
   172  						t.Fatal(err)
   173  					}
   174  
   175  					if r1.Width() != id-k {
   176  						t.Fatalf("Width: %d:%d", r1.Width(), j)
   177  					}
   178  
   179  					r2, err := r1.Resize(fmt.Sprintf("%dx", id-k-1))
   180  					if err != nil {
   181  						t.Fatal(err)
   182  					}
   183  
   184  					_, err = r2.decodeSource()
   185  					if err != nil {
   186  						t.Fatal("Err decode:", err)
   187  					}
   188  
   189  					img = r1
   190  				}
   191  			}
   192  		}(i + 20)
   193  	}
   194  
   195  	wg.Wait()
   196  }
   197  
   198  func TestDecodeImaging(t *testing.T) {
   199  	assert := require.New(t)
   200  	m := map[string]interface{}{
   201  		"quality":        42,
   202  		"resampleFilter": "NearestNeighbor",
   203  		"anchor":         "topLeft",
   204  	}
   205  
   206  	imaging, err := decodeImaging(m)
   207  
   208  	assert.NoError(err)
   209  	assert.Equal(42, imaging.Quality)
   210  	assert.Equal("nearestneighbor", imaging.ResampleFilter)
   211  	assert.Equal("topleft", imaging.Anchor)
   212  
   213  	m = map[string]interface{}{}
   214  
   215  	imaging, err = decodeImaging(m)
   216  	assert.NoError(err)
   217  	assert.Equal(defaultJPEGQuality, imaging.Quality)
   218  	assert.Equal("box", imaging.ResampleFilter)
   219  	assert.Equal("smart", imaging.Anchor)
   220  
   221  	_, err = decodeImaging(map[string]interface{}{
   222  		"quality": 123,
   223  	})
   224  	assert.Error(err)
   225  
   226  	_, err = decodeImaging(map[string]interface{}{
   227  		"resampleFilter": "asdf",
   228  	})
   229  	assert.Error(err)
   230  
   231  	_, err = decodeImaging(map[string]interface{}{
   232  		"anchor": "asdf",
   233  	})
   234  	assert.Error(err)
   235  
   236  	imaging, err = decodeImaging(map[string]interface{}{
   237  		"anchor": "Smart",
   238  	})
   239  	assert.NoError(err)
   240  	assert.Equal("smart", imaging.Anchor)
   241  
   242  }
   243  
   244  func TestImageWithMetadata(t *testing.T) {
   245  	assert := require.New(t)
   246  
   247  	image := fetchSunset(assert)
   248  
   249  	var meta = []map[string]interface{}{
   250  		{
   251  			"title": "My Sunset",
   252  			"name":  "Sunset #:counter",
   253  			"src":   "*.jpg",
   254  		},
   255  	}
   256  
   257  	assert.NoError(AssignMetadata(meta, image))
   258  	assert.Equal("Sunset #1", image.Name())
   259  
   260  	resized, err := image.Resize("200x")
   261  	assert.NoError(err)
   262  	assert.Equal("Sunset #1", resized.Name())
   263  
   264  }
   265  
   266  func TestImageResize8BitPNG(t *testing.T) {
   267  
   268  	assert := require.New(t)
   269  
   270  	image := fetchImage(assert, "gohugoio.png")
   271  
   272  	assert.Equal(imaging.PNG, image.format)
   273  	assert.Equal("/a/gohugoio.png", image.RelPermalink())
   274  	assert.Equal("image", image.ResourceType())
   275  
   276  	resized, err := image.Resize("800x")
   277  	assert.NoError(err)
   278  	assert.Equal(imaging.PNG, resized.format)
   279  	assert.Equal("/a/gohugoio_hu0e1b9e4a4be4d6f86c7b37b9ccce3fbc_73886_800x0_resize_linear_2.png", resized.RelPermalink())
   280  	assert.Equal(800, resized.Width())
   281  
   282  }
   283  
   284  func TestImageResizeInSubPath(t *testing.T) {
   285  
   286  	assert := require.New(t)
   287  
   288  	image := fetchImage(assert, "sub/gohugoio2.png")
   289  
   290  	assert.Equal(imaging.PNG, image.format)
   291  	assert.Equal("/a/sub/gohugoio2.png", image.RelPermalink())
   292  	assert.Equal("image", image.ResourceType())
   293  
   294  	resized, err := image.Resize("101x101")
   295  	assert.NoError(err)
   296  	assert.Equal(imaging.PNG, resized.format)
   297  	assert.Equal("/a/sub/gohugoio2_hu0e1b9e4a4be4d6f86c7b37b9ccce3fbc_73886_101x101_resize_linear_2.png", resized.RelPermalink())
   298  	assert.Equal(101, resized.Width())
   299  
   300  	assertFileCache(assert, image.spec.BaseFs.Resources.Fs, resized.RelPermalink(), 101, 101)
   301  	publishedImageFilename := filepath.Clean(resized.RelPermalink())
   302  	assertImageFile(assert, image.spec.BaseFs.PublishFs, publishedImageFilename, 101, 101)
   303  	assert.NoError(image.spec.BaseFs.PublishFs.Remove(publishedImageFilename))
   304  
   305  	// Cleare mem cache to simulate reading from the file cache.
   306  	resized.spec.imageCache.clear()
   307  
   308  	resizedAgain, err := image.Resize("101x101")
   309  	assert.NoError(err)
   310  	assert.Equal("/a/sub/gohugoio2_hu0e1b9e4a4be4d6f86c7b37b9ccce3fbc_73886_101x101_resize_linear_2.png", resizedAgain.RelPermalink())
   311  	assert.Equal(101, resizedAgain.Width())
   312  	assertFileCache(assert, image.spec.BaseFs.Resources.Fs, resizedAgain.RelPermalink(), 101, 101)
   313  	assertImageFile(assert, image.spec.BaseFs.PublishFs, publishedImageFilename, 101, 101)
   314  
   315  }
   316  
   317  func TestSVGImage(t *testing.T) {
   318  	assert := require.New(t)
   319  	spec := newTestResourceSpec(assert)
   320  	svg := fetchResourceForSpec(spec, assert, "circle.svg")
   321  	assert.NotNil(svg)
   322  }
   323  
   324  func TestSVGImageContent(t *testing.T) {
   325  	assert := require.New(t)
   326  	spec := newTestResourceSpec(assert)
   327  	svg := fetchResourceForSpec(spec, assert, "circle.svg")
   328  	assert.NotNil(svg)
   329  
   330  	content, err := svg.Content()
   331  	assert.NoError(err)
   332  	assert.IsType("", content)
   333  	assert.Contains(content.(string), `<svg height="100" width="100">`)
   334  }
   335  
   336  func BenchmarkResizeParallel(b *testing.B) {
   337  	assert := require.New(b)
   338  	img := fetchSunset(assert)
   339  
   340  	b.RunParallel(func(pb *testing.PB) {
   341  		for pb.Next() {
   342  			w := rand.Intn(10) + 10
   343  			resized, err := img.Resize(strconv.Itoa(w) + "x")
   344  			if err != nil {
   345  				b.Fatal(err)
   346  			}
   347  			_, err = resized.Resize(strconv.Itoa(w-1) + "x")
   348  			if err != nil {
   349  				b.Fatal(err)
   350  			}
   351  		}
   352  	})
   353  }