resource_test.go (9853B)
1 // Copyright 2019 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 resources
15
16 import (
17 "fmt"
18 "math/rand"
19 "path/filepath"
20 "strings"
21 "testing"
22
23 "github.com/spf13/afero"
24
25 "github.com/gohugoio/hugo/resources/resource"
26
27 "github.com/gohugoio/hugo/media"
28
29 qt "github.com/frankban/quicktest"
30 )
31
32 func TestGenericResource(t *testing.T) {
33 c := qt.New(t)
34 spec := newTestResourceSpec(specDescriptor{c: c})
35
36 r := spec.newGenericResource(nil, nil, nil, "/a/foo.css", "foo.css", media.CSSType)
37
38 c.Assert(r.Permalink(), qt.Equals, "https://example.com/foo.css")
39 c.Assert(r.RelPermalink(), qt.Equals, "/foo.css")
40 c.Assert(r.ResourceType(), qt.Equals, "text")
41 }
42
43 func TestGenericResourceWithLinkFactory(t *testing.T) {
44 c := qt.New(t)
45 spec := newTestResourceSpec(specDescriptor{c: c})
46
47 factory := newTargetPaths("/foo")
48
49 r := spec.newGenericResource(nil, factory, nil, "/a/foo.css", "foo.css", media.CSSType)
50
51 c.Assert(r.Permalink(), qt.Equals, "https://example.com/foo/foo.css")
52 c.Assert(r.RelPermalink(), qt.Equals, "/foo/foo.css")
53 c.Assert(r.Key(), qt.Equals, "/foo/foo.css")
54 c.Assert(r.ResourceType(), qt.Equals, "text")
55 }
56
57 func TestNewResourceFromFilename(t *testing.T) {
58 c := qt.New(t)
59 spec := newTestResourceSpec(specDescriptor{c: c})
60
61 writeSource(t, spec.Fs, "content/a/b/logo.png", "image")
62 writeSource(t, spec.Fs, "content/a/b/data.json", "json")
63
64 bfs := afero.NewBasePathFs(spec.Fs.Source, "content")
65
66 r, err := spec.New(ResourceSourceDescriptor{Fs: bfs, SourceFilename: "a/b/logo.png"})
67
68 c.Assert(err, qt.IsNil)
69 c.Assert(r, qt.Not(qt.IsNil))
70 c.Assert(r.ResourceType(), qt.Equals, "image")
71 c.Assert(r.RelPermalink(), qt.Equals, "/a/b/logo.png")
72 c.Assert(r.Permalink(), qt.Equals, "https://example.com/a/b/logo.png")
73
74 r, err = spec.New(ResourceSourceDescriptor{Fs: bfs, SourceFilename: "a/b/data.json"})
75
76 c.Assert(err, qt.IsNil)
77 c.Assert(r, qt.Not(qt.IsNil))
78 c.Assert(r.ResourceType(), qt.Equals, "application")
79 }
80
81 func TestNewResourceFromFilenameSubPathInBaseURL(t *testing.T) {
82 c := qt.New(t)
83 spec := newTestResourceSpec(specDescriptor{c: c, baseURL: "https://example.com/docs"})
84
85 writeSource(t, spec.Fs, "content/a/b/logo.png", "image")
86 bfs := afero.NewBasePathFs(spec.Fs.Source, "content")
87
88 fmt.Println()
89 r, err := spec.New(ResourceSourceDescriptor{Fs: bfs, SourceFilename: filepath.FromSlash("a/b/logo.png")})
90
91 c.Assert(err, qt.IsNil)
92 c.Assert(r, qt.Not(qt.IsNil))
93 c.Assert(r.ResourceType(), qt.Equals, "image")
94 c.Assert(r.RelPermalink(), qt.Equals, "/docs/a/b/logo.png")
95 c.Assert(r.Permalink(), qt.Equals, "https://example.com/docs/a/b/logo.png")
96 }
97
98 var pngType, _ = media.FromStringAndExt("image/png", "png")
99
100 func TestResourcesByType(t *testing.T) {
101 c := qt.New(t)
102 spec := newTestResourceSpec(specDescriptor{c: c})
103 resources := resource.Resources{
104 spec.newGenericResource(nil, nil, nil, "/a/foo1.css", "foo1.css", media.CSSType),
105 spec.newGenericResource(nil, nil, nil, "/a/logo.png", "logo.css", pngType),
106 spec.newGenericResource(nil, nil, nil, "/a/foo2.css", "foo2.css", media.CSSType),
107 spec.newGenericResource(nil, nil, nil, "/a/foo3.css", "foo3.css", media.CSSType),
108 }
109
110 c.Assert(len(resources.ByType("text")), qt.Equals, 3)
111 c.Assert(len(resources.ByType("image")), qt.Equals, 1)
112 }
113
114 func TestResourcesGetByPrefix(t *testing.T) {
115 c := qt.New(t)
116 spec := newTestResourceSpec(specDescriptor{c: c})
117 resources := resource.Resources{
118 spec.newGenericResource(nil, nil, nil, "/a/foo1.css", "foo1.css", media.CSSType),
119 spec.newGenericResource(nil, nil, nil, "/a/logo1.png", "logo1.png", pngType),
120 spec.newGenericResource(nil, nil, nil, "/b/Logo2.png", "Logo2.png", pngType),
121 spec.newGenericResource(nil, nil, nil, "/b/foo2.css", "foo2.css", media.CSSType),
122 spec.newGenericResource(nil, nil, nil, "/b/foo3.css", "foo3.css", media.CSSType),
123 }
124
125 c.Assert(resources.GetMatch("asdf*"), qt.IsNil)
126 c.Assert(resources.GetMatch("logo*").RelPermalink(), qt.Equals, "/logo1.png")
127 c.Assert(resources.GetMatch("loGo*").RelPermalink(), qt.Equals, "/logo1.png")
128 c.Assert(resources.GetMatch("logo2*").RelPermalink(), qt.Equals, "/Logo2.png")
129 c.Assert(resources.GetMatch("foo2*").RelPermalink(), qt.Equals, "/foo2.css")
130 c.Assert(resources.GetMatch("foo1*").RelPermalink(), qt.Equals, "/foo1.css")
131 c.Assert(resources.GetMatch("foo1*").RelPermalink(), qt.Equals, "/foo1.css")
132 c.Assert(resources.GetMatch("asdfasdf*"), qt.IsNil)
133
134 c.Assert(len(resources.Match("logo*")), qt.Equals, 2)
135 c.Assert(len(resources.Match("logo2*")), qt.Equals, 1)
136
137 logo := resources.GetMatch("logo*")
138 c.Assert(logo.Params(), qt.Not(qt.IsNil))
139 c.Assert(logo.Name(), qt.Equals, "logo1.png")
140 c.Assert(logo.Title(), qt.Equals, "logo1.png")
141 }
142
143 func TestResourcesGetMatch(t *testing.T) {
144 c := qt.New(t)
145 spec := newTestResourceSpec(specDescriptor{c: c})
146 resources := resource.Resources{
147 spec.newGenericResource(nil, nil, nil, "/a/foo1.css", "foo1.css", media.CSSType),
148 spec.newGenericResource(nil, nil, nil, "/a/logo1.png", "logo1.png", pngType),
149 spec.newGenericResource(nil, nil, nil, "/b/Logo2.png", "Logo2.png", pngType),
150 spec.newGenericResource(nil, nil, nil, "/b/foo2.css", "foo2.css", media.CSSType),
151 spec.newGenericResource(nil, nil, nil, "/b/foo3.css", "foo3.css", media.CSSType),
152 spec.newGenericResource(nil, nil, nil, "/b/c/foo4.css", "c/foo4.css", media.CSSType),
153 spec.newGenericResource(nil, nil, nil, "/b/c/foo5.css", "c/foo5.css", media.CSSType),
154 spec.newGenericResource(nil, nil, nil, "/b/c/d/foo6.css", "c/d/foo6.css", media.CSSType),
155 }
156
157 c.Assert(resources.GetMatch("logo*").RelPermalink(), qt.Equals, "/logo1.png")
158 c.Assert(resources.GetMatch("loGo*").RelPermalink(), qt.Equals, "/logo1.png")
159 c.Assert(resources.GetMatch("logo2*").RelPermalink(), qt.Equals, "/Logo2.png")
160 c.Assert(resources.GetMatch("foo2*").RelPermalink(), qt.Equals, "/foo2.css")
161 c.Assert(resources.GetMatch("foo1*").RelPermalink(), qt.Equals, "/foo1.css")
162 c.Assert(resources.GetMatch("foo1*").RelPermalink(), qt.Equals, "/foo1.css")
163 c.Assert(resources.GetMatch("*/foo*").RelPermalink(), qt.Equals, "/c/foo4.css")
164
165 c.Assert(resources.GetMatch("asdfasdf"), qt.IsNil)
166
167 c.Assert(len(resources.Match("Logo*")), qt.Equals, 2)
168 c.Assert(len(resources.Match("logo2*")), qt.Equals, 1)
169 c.Assert(len(resources.Match("c/*")), qt.Equals, 2)
170
171 c.Assert(len(resources.Match("**.css")), qt.Equals, 6)
172 c.Assert(len(resources.Match("**/*.css")), qt.Equals, 3)
173 c.Assert(len(resources.Match("c/**/*.css")), qt.Equals, 1)
174
175 // Matches only CSS files in c/
176 c.Assert(len(resources.Match("c/**.css")), qt.Equals, 3)
177
178 // Matches all CSS files below c/ (including in c/d/)
179 c.Assert(len(resources.Match("c/**.css")), qt.Equals, 3)
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 c.Assert(len(resources.Match("/c/**.css")), qt.Equals, 0)
186 }
187
188 func BenchmarkResourcesMatch(b *testing.B) {
189 resources := benchResources(b)
190 prefixes := []string{"abc*", "jkl*", "nomatch*", "sub/*"}
191
192 b.RunParallel(func(pb *testing.PB) {
193 for pb.Next() {
194 resources.Match(prefixes[rand.Intn(len(prefixes))])
195 }
196 })
197 }
198
199 // This adds a benchmark for the a100 test case as described by Russ Cox here:
200 // https://research.swtch.com/glob (really interesting article)
201 // I don't expect Hugo users to "stumble upon" this problem, so this is more to satisfy
202 // my own curiosity.
203 func BenchmarkResourcesMatchA100(b *testing.B) {
204 c := qt.New(b)
205 spec := newTestResourceSpec(specDescriptor{c: c})
206 a100 := strings.Repeat("a", 100)
207 pattern := "a*a*a*a*a*a*a*a*b"
208
209 resources := resource.Resources{spec.newGenericResource(nil, nil, nil, "/a/"+a100, a100, media.CSSType)}
210
211 b.ResetTimer()
212 for i := 0; i < b.N; i++ {
213 resources.Match(pattern)
214 }
215 }
216
217 func benchResources(b *testing.B) resource.Resources {
218 c := qt.New(b)
219 spec := newTestResourceSpec(specDescriptor{c: c})
220 var resources resource.Resources
221
222 for i := 0; i < 30; i++ {
223 name := fmt.Sprintf("abcde%d_%d.css", i%5, i)
224 resources = append(resources, spec.newGenericResource(nil, nil, nil, "/a/"+name, name, media.CSSType))
225 }
226
227 for i := 0; i < 30; i++ {
228 name := fmt.Sprintf("efghi%d_%d.css", i%5, i)
229 resources = append(resources, spec.newGenericResource(nil, nil, nil, "/a/"+name, name, media.CSSType))
230 }
231
232 for i := 0; i < 30; i++ {
233 name := fmt.Sprintf("jklmn%d_%d.css", i%5, i)
234 resources = append(resources, spec.newGenericResource(nil, nil, nil, "/b/sub/"+name, "sub/"+name, media.CSSType))
235 }
236
237 return resources
238 }
239
240 func BenchmarkAssignMetadata(b *testing.B) {
241 c := qt.New(b)
242 spec := newTestResourceSpec(specDescriptor{c: c})
243
244 for i := 0; i < b.N; i++ {
245 b.StopTimer()
246 var resources resource.Resources
247 meta := []map[string]any{
248 {
249 "title": "Foo #:counter",
250 "name": "Foo Name #:counter",
251 "src": "foo1*",
252 },
253 {
254 "title": "Rest #:counter",
255 "name": "Rest Name #:counter",
256 "src": "*",
257 },
258 }
259 for i := 0; i < 20; i++ {
260 name := fmt.Sprintf("foo%d_%d.css", i%5, i)
261 resources = append(resources, spec.newGenericResource(nil, nil, nil, "/a/"+name, name, media.CSSType))
262 }
263 b.StartTimer()
264
265 if err := AssignMetadata(meta, resources...); err != nil {
266 b.Fatal(err)
267 }
268
269 }
270 }