github.com/olliephillips/hugo@v0.42.2/resource/resource_test.go (about) 1 // Copyright 2017-present 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" 20 "path/filepath" 21 "strings" 22 "testing" 23 "time" 24 25 "github.com/stretchr/testify/require" 26 ) 27 28 func TestGenericResource(t *testing.T) { 29 assert := require.New(t) 30 spec := newTestResourceSpec(assert) 31 32 r := spec.newGenericResource(nil, nil, "/a/foo.css", "foo.css", "css") 33 34 assert.Equal("https://example.com/foo.css", r.Permalink()) 35 assert.Equal("/foo.css", r.RelPermalink()) 36 assert.Equal("css", r.ResourceType()) 37 38 } 39 40 func TestGenericResourceWithLinkFacory(t *testing.T) { 41 assert := require.New(t) 42 spec := newTestResourceSpec(assert) 43 44 factory := func(s string) string { 45 return path.Join("/foo", s) 46 } 47 r := spec.newGenericResource(factory, nil, "/a/foo.css", "foo.css", "css") 48 49 assert.Equal("https://example.com/foo/foo.css", r.Permalink()) 50 assert.Equal("/foo/foo.css", r.RelPermalink()) 51 assert.Equal("css", r.ResourceType()) 52 } 53 54 func TestNewResourceFromFilename(t *testing.T) { 55 assert := require.New(t) 56 spec := newTestResourceSpec(assert) 57 58 writeSource(t, spec.Fs, "content/a/b/logo.png", "image") 59 writeSource(t, spec.Fs, "content/a/b/data.json", "json") 60 61 r, err := spec.NewResourceFromFilename(nil, 62 filepath.FromSlash("a/b/logo.png"), filepath.FromSlash("a/b/logo.png")) 63 64 assert.NoError(err) 65 assert.NotNil(r) 66 assert.Equal("image", r.ResourceType()) 67 assert.Equal("/a/b/logo.png", r.RelPermalink()) 68 assert.Equal("https://example.com/a/b/logo.png", r.Permalink()) 69 70 r, err = spec.NewResourceFromFilename(nil, "a/b/data.json", "a/b/data.json") 71 72 assert.NoError(err) 73 assert.NotNil(r) 74 assert.Equal("json", r.ResourceType()) 75 76 cloned := r.(Cloner).WithNewBase("aceof") 77 assert.Equal(r.ResourceType(), cloned.ResourceType()) 78 assert.Equal("/aceof/a/b/data.json", cloned.RelPermalink()) 79 } 80 81 func TestNewResourceFromFilenameSubPathInBaseURL(t *testing.T) { 82 assert := require.New(t) 83 spec := newTestResourceSpecForBaseURL(assert, "https://example.com/docs") 84 85 writeSource(t, spec.Fs, "content/a/b/logo.png", "image") 86 87 r, err := spec.NewResourceFromFilename(nil, 88 filepath.FromSlash("a/b/logo.png"), filepath.FromSlash("a/b/logo.png")) 89 90 assert.NoError(err) 91 assert.NotNil(r) 92 assert.Equal("image", r.ResourceType()) 93 assert.Equal("/docs/a/b/logo.png", r.RelPermalink()) 94 assert.Equal("https://example.com/docs/a/b/logo.png", r.Permalink()) 95 img := r.(*Image) 96 assert.Equal(filepath.FromSlash("/a/b/logo.png"), img.target()) 97 98 } 99 100 func TestResourcesByType(t *testing.T) { 101 assert := require.New(t) 102 spec := newTestResourceSpec(assert) 103 resources := Resources{ 104 spec.newGenericResource(nil, nil, "/a/foo1.css", "foo1.css", "css"), 105 spec.newGenericResource(nil, nil, "/a/logo.png", "logo.css", "image"), 106 spec.newGenericResource(nil, nil, "/a/foo2.css", "foo2.css", "css"), 107 spec.newGenericResource(nil, nil, "/a/foo3.css", "foo3.css", "css")} 108 109 assert.Len(resources.ByType("css"), 3) 110 assert.Len(resources.ByType("image"), 1) 111 112 } 113 114 func TestResourcesGetByPrefix(t *testing.T) { 115 assert := require.New(t) 116 spec := newTestResourceSpec(assert) 117 resources := Resources{ 118 spec.newGenericResource(nil, nil, "/a/foo1.css", "foo1.css", "css"), 119 spec.newGenericResource(nil, nil, "/a/logo1.png", "logo1.png", "image"), 120 spec.newGenericResource(nil, nil, "/b/Logo2.png", "Logo2.png", "image"), 121 spec.newGenericResource(nil, nil, "/b/foo2.css", "foo2.css", "css"), 122 spec.newGenericResource(nil, nil, "/b/foo3.css", "foo3.css", "css")} 123 124 assert.Nil(resources.GetByPrefix("asdf")) 125 assert.Equal("/logo1.png", resources.GetByPrefix("logo").RelPermalink()) 126 assert.Equal("/logo1.png", resources.GetByPrefix("loGo").RelPermalink()) 127 assert.Equal("/Logo2.png", resources.GetByPrefix("logo2").RelPermalink()) 128 assert.Equal("/foo2.css", resources.GetByPrefix("foo2").RelPermalink()) 129 assert.Equal("/foo1.css", resources.GetByPrefix("foo1").RelPermalink()) 130 assert.Equal("/foo1.css", resources.GetByPrefix("foo1").RelPermalink()) 131 assert.Nil(resources.GetByPrefix("asdfasdf")) 132 133 assert.Equal(2, len(resources.ByPrefix("logo"))) 134 assert.Equal(1, len(resources.ByPrefix("logo2"))) 135 136 logo := resources.GetByPrefix("logo") 137 assert.NotNil(logo.Params()) 138 assert.Equal("logo1.png", logo.Name()) 139 assert.Equal("logo1.png", logo.Title()) 140 141 } 142 143 func TestResourcesGetMatch(t *testing.T) { 144 assert := require.New(t) 145 spec := newTestResourceSpec(assert) 146 resources := Resources{ 147 spec.newGenericResource(nil, nil, "/a/foo1.css", "foo1.css", "css"), 148 spec.newGenericResource(nil, nil, "/a/logo1.png", "logo1.png", "image"), 149 spec.newGenericResource(nil, nil, "/b/Logo2.png", "Logo2.png", "image"), 150 spec.newGenericResource(nil, nil, "/b/foo2.css", "foo2.css", "css"), 151 spec.newGenericResource(nil, nil, "/b/foo3.css", "foo3.css", "css"), 152 spec.newGenericResource(nil, nil, "/b/c/foo4.css", "c/foo4.css", "css"), 153 spec.newGenericResource(nil, nil, "/b/c/foo5.css", "c/foo5.css", "css"), 154 spec.newGenericResource(nil, nil, "/b/c/d/foo6.css", "c/d/foo6.css", "css"), 155 } 156 157 assert.Equal("/logo1.png", resources.GetMatch("logo*").RelPermalink()) 158 assert.Equal("/logo1.png", resources.GetMatch("loGo*").RelPermalink()) 159 assert.Equal("/Logo2.png", resources.GetMatch("logo2*").RelPermalink()) 160 assert.Equal("/foo2.css", resources.GetMatch("foo2*").RelPermalink()) 161 assert.Equal("/foo1.css", resources.GetMatch("foo1*").RelPermalink()) 162 assert.Equal("/foo1.css", resources.GetMatch("foo1*").RelPermalink()) 163 assert.Equal("/c/foo4.css", resources.GetMatch("*/foo*").RelPermalink()) 164 165 assert.Nil(resources.GetMatch("asdfasdf")) 166 167 assert.Equal(2, len(resources.Match("Logo*"))) 168 assert.Equal(1, len(resources.Match("logo2*"))) 169 assert.Equal(2, len(resources.Match("c/*"))) 170 171 assert.Equal(6, len(resources.Match("**.css"))) 172 assert.Equal(3, len(resources.Match("**/*.css"))) 173 assert.Equal(1, len(resources.Match("c/**/*.css"))) 174 175 // Matches only CSS files in c/ 176 assert.Equal(3, len(resources.Match("c/**.css"))) 177 178 // Matches all CSS files below c/ (including in c/d/) 179 assert.Equal(3, len(resources.Match("c/**.css"))) 180 181 // Patterns beginning with a slash will not match anything. 182 // We could maybe consider trimming that slash, but let's be explicit about this. 183 // (it is possible for users to do a rename) 184 // This is analogous to standing in a directory and doing "ls *.*". 185 assert.Equal(0, len(resources.Match("/c/**.css"))) 186 187 } 188 189 func TestAssignMetadata(t *testing.T) { 190 assert := require.New(t) 191 spec := newTestResourceSpec(assert) 192 193 var foo1, foo2, foo3, logo1, logo2, logo3 Resource 194 var resources Resources 195 196 for _, this := range []struct { 197 metaData []map[string]interface{} 198 assertFunc func(err error) 199 }{ 200 {[]map[string]interface{}{ 201 { 202 "title": "My Resource", 203 "name": "My Name", 204 "src": "*", 205 }, 206 }, func(err error) { 207 assert.Equal("My Resource", logo1.Title()) 208 assert.Equal("My Name", logo1.Name()) 209 assert.Equal("My Name", foo2.Name()) 210 211 }}, 212 {[]map[string]interface{}{ 213 { 214 "title": "My Logo", 215 "src": "*loGo*", 216 }, 217 { 218 "title": "My Resource", 219 "name": "My Name", 220 "src": "*", 221 }, 222 }, func(err error) { 223 assert.Equal("My Logo", logo1.Title()) 224 assert.Equal("My Logo", logo2.Title()) 225 assert.Equal("My Name", logo1.Name()) 226 assert.Equal("My Name", foo2.Name()) 227 assert.Equal("My Name", foo3.Name()) 228 assert.Equal("My Resource", foo3.Title()) 229 230 }}, 231 {[]map[string]interface{}{ 232 { 233 "title": "My Logo", 234 "src": "*loGo*", 235 "params": map[string]interface{}{ 236 "Param1": true, 237 "icon": "logo", 238 }, 239 }, 240 { 241 "title": "My Resource", 242 "src": "*", 243 "params": map[string]interface{}{ 244 "Param2": true, 245 "icon": "resource", 246 }, 247 }, 248 }, func(err error) { 249 assert.NoError(err) 250 assert.Equal("My Logo", logo1.Title()) 251 assert.Equal("My Resource", foo3.Title()) 252 _, p1 := logo2.Params()["param1"] 253 _, p2 := foo2.Params()["param2"] 254 _, p1_2 := foo2.Params()["param1"] 255 _, p2_2 := logo2.Params()["param2"] 256 257 icon1, _ := logo2.Params()["icon"] 258 icon2, _ := foo2.Params()["icon"] 259 260 assert.True(p1) 261 assert.True(p2) 262 263 // Check merge 264 assert.True(p2_2) 265 assert.False(p1_2) 266 267 assert.Equal("logo", icon1) 268 assert.Equal("resource", icon2) 269 270 }}, 271 {[]map[string]interface{}{ 272 { 273 "name": "Logo Name #:counter", 274 "src": "*logo*", 275 }, 276 { 277 "title": "Resource #:counter", 278 "name": "Name #:counter", 279 "src": "*", 280 }, 281 }, func(err error) { 282 assert.NoError(err) 283 assert.Equal("Resource #2", logo2.Title()) 284 assert.Equal("Logo Name #1", logo2.Name()) 285 assert.Equal("Resource #4", logo1.Title()) 286 assert.Equal("Logo Name #2", logo1.Name()) 287 assert.Equal("Resource #1", foo2.Title()) 288 assert.Equal("Resource #3", foo1.Title()) 289 assert.Equal("Name #2", foo1.Name()) 290 assert.Equal("Resource #5", foo3.Title()) 291 292 assert.Equal(logo2, resources.GetByPrefix("logo name #1")) 293 294 }}, 295 {[]map[string]interface{}{ 296 { 297 "title": "Third Logo #:counter", 298 "src": "logo3.png", 299 }, 300 { 301 "title": "Other Logo #:counter", 302 "name": "Name #:counter", 303 "src": "logo*", 304 }, 305 }, func(err error) { 306 assert.NoError(err) 307 assert.Equal("Third Logo #1", logo3.Title()) 308 assert.Equal("Name #3", logo3.Name()) 309 assert.Equal("Other Logo #1", logo2.Title()) 310 assert.Equal("Name #1", logo2.Name()) 311 assert.Equal("Other Logo #2", logo1.Title()) 312 assert.Equal("Name #2", logo1.Name()) 313 314 }}, 315 {[]map[string]interface{}{ 316 { 317 "title": "Third Logo", 318 "src": "logo3.png", 319 }, 320 { 321 "title": "Other Logo #:counter", 322 "name": "Name #:counter", 323 "src": "logo*", 324 }, 325 }, func(err error) { 326 assert.NoError(err) 327 assert.Equal("Third Logo", logo3.Title()) 328 assert.Equal("Name #3", logo3.Name()) 329 assert.Equal("Other Logo #1", logo2.Title()) 330 assert.Equal("Name #1", logo2.Name()) 331 assert.Equal("Other Logo #2", logo1.Title()) 332 assert.Equal("Name #2", logo1.Name()) 333 334 }}, 335 {[]map[string]interface{}{ 336 { 337 "name": "third-logo", 338 "src": "logo3.png", 339 }, 340 { 341 "title": "Logo #:counter", 342 "name": "Name #:counter", 343 "src": "logo*", 344 }, 345 }, func(err error) { 346 assert.NoError(err) 347 assert.Equal("Logo #3", logo3.Title()) 348 assert.Equal("third-logo", logo3.Name()) 349 assert.Equal("Logo #1", logo2.Title()) 350 assert.Equal("Name #1", logo2.Name()) 351 assert.Equal("Logo #2", logo1.Title()) 352 assert.Equal("Name #2", logo1.Name()) 353 354 }}, 355 {[]map[string]interface{}{ 356 { 357 "title": "Third Logo #:counter", 358 }, 359 }, func(err error) { 360 // Missing src 361 assert.Error(err) 362 363 }}, 364 {[]map[string]interface{}{ 365 { 366 "title": "Title", 367 "src": "[]", 368 }, 369 }, func(err error) { 370 // Invalid pattern 371 assert.Error(err) 372 373 }}, 374 } { 375 376 foo2 = spec.newGenericResource(nil, nil, "/b/foo2.css", "foo2.css", "css") 377 logo2 = spec.newGenericResource(nil, nil, "/b/Logo2.png", "Logo2.png", "image") 378 foo1 = spec.newGenericResource(nil, nil, "/a/foo1.css", "foo1.css", "css") 379 logo1 = spec.newGenericResource(nil, nil, "/a/logo1.png", "logo1.png", "image") 380 foo3 = spec.newGenericResource(nil, nil, "/b/foo3.css", "foo3.css", "css") 381 logo3 = spec.newGenericResource(nil, nil, "/b/logo3.png", "logo3.png", "image") 382 383 resources = Resources{ 384 foo2, 385 logo2, 386 foo1, 387 logo1, 388 foo3, 389 logo3, 390 } 391 392 this.assertFunc(AssignMetadata(this.metaData, resources...)) 393 } 394 395 } 396 397 func BenchmarkResourcesByPrefix(b *testing.B) { 398 resources := benchResources(b) 399 prefixes := []string{"abc", "jkl", "nomatch", "sub/"} 400 rnd := rand.New(rand.NewSource(time.Now().Unix())) 401 402 b.RunParallel(func(pb *testing.PB) { 403 for pb.Next() { 404 resources.ByPrefix(prefixes[rnd.Intn(len(prefixes))]) 405 } 406 }) 407 } 408 409 func BenchmarkResourcesMatch(b *testing.B) { 410 resources := benchResources(b) 411 prefixes := []string{"abc*", "jkl*", "nomatch*", "sub/*"} 412 rnd := rand.New(rand.NewSource(time.Now().Unix())) 413 414 b.RunParallel(func(pb *testing.PB) { 415 for pb.Next() { 416 resources.Match(prefixes[rnd.Intn(len(prefixes))]) 417 } 418 }) 419 } 420 421 // This adds a benchmark for the a100 test case as described by Russ Cox here: 422 // https://research.swtch.com/glob (really interesting article) 423 // I don't expect Hugo users to "stumble upon" this problem, so this is more to satisfy 424 // my own curiosity. 425 func BenchmarkResourcesMatchA100(b *testing.B) { 426 assert := require.New(b) 427 spec := newTestResourceSpec(assert) 428 a100 := strings.Repeat("a", 100) 429 pattern := "a*a*a*a*a*a*a*a*b" 430 431 resources := Resources{spec.newGenericResource(nil, nil, "/a/"+a100, a100, "css")} 432 433 b.ResetTimer() 434 for i := 0; i < b.N; i++ { 435 resources.Match(pattern) 436 } 437 438 } 439 440 func benchResources(b *testing.B) Resources { 441 assert := require.New(b) 442 spec := newTestResourceSpec(assert) 443 var resources Resources 444 445 for i := 0; i < 30; i++ { 446 name := fmt.Sprintf("abcde%d_%d.css", i%5, i) 447 resources = append(resources, spec.newGenericResource(nil, nil, "/a/"+name, name, "css")) 448 } 449 450 for i := 0; i < 30; i++ { 451 name := fmt.Sprintf("efghi%d_%d.css", i%5, i) 452 resources = append(resources, spec.newGenericResource(nil, nil, "/a/"+name, name, "css")) 453 } 454 455 for i := 0; i < 30; i++ { 456 name := fmt.Sprintf("jklmn%d_%d.css", i%5, i) 457 resources = append(resources, spec.newGenericResource(nil, nil, "/b/sub/"+name, "sub/"+name, "css")) 458 } 459 460 return resources 461 462 } 463 464 func BenchmarkAssignMetadata(b *testing.B) { 465 assert := require.New(b) 466 spec := newTestResourceSpec(assert) 467 468 for i := 0; i < b.N; i++ { 469 b.StopTimer() 470 var resources Resources 471 var meta = []map[string]interface{}{ 472 { 473 "title": "Foo #:counter", 474 "name": "Foo Name #:counter", 475 "src": "foo1*", 476 }, 477 { 478 "title": "Rest #:counter", 479 "name": "Rest Name #:counter", 480 "src": "*", 481 }, 482 } 483 for i := 0; i < 20; i++ { 484 name := fmt.Sprintf("foo%d_%d.css", i%5, i) 485 resources = append(resources, spec.newGenericResource(nil, nil, "/a/"+name, name, "css")) 486 } 487 b.StartTimer() 488 489 if err := AssignMetadata(meta, resources...); err != nil { 490 b.Fatal(err) 491 } 492 493 } 494 }