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 }