github.com/gohugoio/hugo@v0.88.1/config/defaultConfigProvider_test.go (about) 1 // Copyright 2021 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 config 15 16 import ( 17 "context" 18 "errors" 19 "fmt" 20 "strconv" 21 "strings" 22 "testing" 23 24 "github.com/spf13/viper" 25 26 "github.com/gohugoio/hugo/common/para" 27 28 "github.com/gohugoio/hugo/common/maps" 29 30 qt "github.com/frankban/quicktest" 31 ) 32 33 func TestDefaultConfigProvider(t *testing.T) { 34 c := qt.New(t) 35 36 c.Run("Set and get", func(c *qt.C) { 37 cfg := New() 38 var k string 39 var v interface{} 40 41 k, v = "foo", "bar" 42 cfg.Set(k, v) 43 c.Assert(cfg.Get(k), qt.Equals, v) 44 c.Assert(cfg.Get(strings.ToUpper(k)), qt.Equals, v) 45 c.Assert(cfg.GetString(k), qt.Equals, v) 46 47 k, v = "foo", 42 48 cfg.Set(k, v) 49 c.Assert(cfg.Get(k), qt.Equals, v) 50 c.Assert(cfg.GetInt(k), qt.Equals, v) 51 52 c.Assert(cfg.Get(""), qt.DeepEquals, maps.Params{ 53 "foo": 42, 54 }) 55 }) 56 57 c.Run("Set and get map", func(c *qt.C) { 58 cfg := New() 59 60 cfg.Set("foo", map[string]interface{}{ 61 "bar": "baz", 62 }) 63 64 c.Assert(cfg.Get("foo"), qt.DeepEquals, maps.Params{ 65 "bar": "baz", 66 }) 67 68 c.Assert(cfg.GetStringMap("foo"), qt.DeepEquals, map[string]interface{}{"bar": string("baz")}) 69 c.Assert(cfg.GetStringMapString("foo"), qt.DeepEquals, map[string]string{"bar": string("baz")}) 70 }) 71 72 c.Run("Set and get nested", func(c *qt.C) { 73 cfg := New() 74 75 cfg.Set("a", map[string]interface{}{ 76 "B": "bv", 77 }) 78 cfg.Set("a.c", "cv") 79 80 c.Assert(cfg.Get("a"), qt.DeepEquals, maps.Params{ 81 "b": "bv", 82 "c": "cv", 83 }) 84 c.Assert(cfg.Get("a.c"), qt.Equals, "cv") 85 86 cfg.Set("b.a", "av") 87 c.Assert(cfg.Get("b"), qt.DeepEquals, maps.Params{ 88 "a": "av", 89 }) 90 91 cfg.Set("b", map[string]interface{}{ 92 "b": "bv", 93 }) 94 95 c.Assert(cfg.Get("b"), qt.DeepEquals, maps.Params{ 96 "a": "av", 97 "b": "bv", 98 }) 99 100 cfg = New() 101 102 cfg.Set("a", "av") 103 104 cfg.Set("", map[string]interface{}{ 105 "a": "av2", 106 "b": "bv2", 107 }) 108 109 c.Assert(cfg.Get(""), qt.DeepEquals, maps.Params{ 110 "a": "av2", 111 "b": "bv2", 112 }) 113 114 cfg = New() 115 116 cfg.Set("a", "av") 117 118 cfg.Set("", map[string]interface{}{ 119 "b": "bv2", 120 }) 121 122 c.Assert(cfg.Get(""), qt.DeepEquals, maps.Params{ 123 "a": "av", 124 "b": "bv2", 125 }) 126 127 cfg = New() 128 129 cfg.Set("", map[string]interface{}{ 130 "foo": map[string]interface{}{ 131 "a": "av", 132 }, 133 }) 134 135 cfg.Set("", map[string]interface{}{ 136 "foo": map[string]interface{}{ 137 "b": "bv2", 138 }, 139 }) 140 141 c.Assert(cfg.Get("foo"), qt.DeepEquals, maps.Params{ 142 "a": "av", 143 "b": "bv2", 144 }) 145 }) 146 147 c.Run("Merge default strategy", func(c *qt.C) { 148 cfg := New() 149 150 cfg.Set("a", map[string]interface{}{ 151 "B": "bv", 152 }) 153 154 cfg.Merge("a", map[string]interface{}{ 155 "B": "bv2", 156 "c": "cv2", 157 }) 158 159 c.Assert(cfg.Get("a"), qt.DeepEquals, maps.Params{ 160 "b": "bv", 161 "c": "cv2", 162 }) 163 164 cfg = New() 165 166 cfg.Set("a", "av") 167 168 cfg.Merge("", map[string]interface{}{ 169 "a": "av2", 170 "b": "bv2", 171 }) 172 173 c.Assert(cfg.Get(""), qt.DeepEquals, maps.Params{ 174 "a": "av", 175 }) 176 }) 177 178 c.Run("Merge shallow", func(c *qt.C) { 179 cfg := New() 180 181 cfg.Set("a", map[string]interface{}{ 182 "_merge": "shallow", 183 "B": "bv", 184 "c": map[string]interface{}{ 185 "b": "bv", 186 }, 187 }) 188 189 cfg.Merge("a", map[string]interface{}{ 190 "c": map[string]interface{}{ 191 "d": "dv2", 192 }, 193 "e": "ev2", 194 }) 195 196 c.Assert(cfg.Get("a"), qt.DeepEquals, maps.Params{ 197 "e": "ev2", 198 "_merge": maps.ParamsMergeStrategyShallow, 199 "b": "bv", 200 "c": maps.Params{ 201 "b": "bv", 202 }, 203 }) 204 }) 205 206 // Issue #8679 207 c.Run("Merge typed maps", func(c *qt.C) { 208 209 for _, left := range []interface{}{ 210 map[string]string{ 211 "c": "cv1", 212 }, 213 map[string]interface{}{ 214 "c": "cv1", 215 }, 216 map[interface{}]interface{}{ 217 "c": "cv1", 218 }, 219 } { 220 cfg := New() 221 222 cfg.Set("", map[string]interface{}{ 223 "b": left, 224 }) 225 226 cfg.Merge("", maps.Params{ 227 "b": maps.Params{ 228 "c": "cv2", 229 "d": "dv2", 230 }, 231 }) 232 233 c.Assert(cfg.Get(""), qt.DeepEquals, maps.Params{ 234 "b": maps.Params{ 235 "c": "cv1", 236 "d": "dv2", 237 }, 238 }) 239 } 240 241 for _, left := range []interface{}{ 242 map[string]string{ 243 "b": "bv1", 244 }, 245 map[string]interface{}{ 246 "b": "bv1", 247 }, 248 map[interface{}]interface{}{ 249 "b": "bv1", 250 }, 251 } { 252 253 for _, right := range []interface{}{ 254 map[string]string{ 255 "b": "bv2", 256 "c": "cv2", 257 }, 258 map[string]interface{}{ 259 "b": "bv2", 260 "c": "cv2", 261 }, 262 map[interface{}]interface{}{ 263 "b": "bv2", 264 "c": "cv2", 265 }, 266 } { 267 cfg := New() 268 269 cfg.Set("a", left) 270 271 cfg.Merge("a", right) 272 273 c.Assert(cfg.Get(""), qt.DeepEquals, maps.Params{ 274 "a": maps.Params{ 275 "b": "bv1", 276 "c": "cv2", 277 }, 278 }) 279 } 280 281 } 282 283 }) 284 285 // Issue #8701 286 c.Run("Prevent _merge only maps", func(c *qt.C) { 287 cfg := New() 288 289 cfg.Set("", map[string]interface{}{ 290 "B": "bv", 291 }) 292 293 cfg.Merge("", map[string]interface{}{ 294 "c": map[string]interface{}{ 295 "_merge": "shallow", 296 "d": "dv2", 297 }, 298 }) 299 300 c.Assert(cfg.Get(""), qt.DeepEquals, maps.Params{ 301 "b": "bv", 302 }) 303 }) 304 305 c.Run("IsSet", func(c *qt.C) { 306 cfg := New() 307 308 cfg.Set("a", map[string]interface{}{ 309 "B": "bv", 310 }) 311 312 c.Assert(cfg.IsSet("A"), qt.IsTrue) 313 c.Assert(cfg.IsSet("a.b"), qt.IsTrue) 314 c.Assert(cfg.IsSet("z"), qt.IsFalse) 315 }) 316 317 c.Run("Para", func(c *qt.C) { 318 cfg := New() 319 p := para.New(4) 320 r, _ := p.Start(context.Background()) 321 322 setAndGet := func(k string, v int) error { 323 vs := strconv.Itoa(v) 324 cfg.Set(k, v) 325 err := errors.New("get failed") 326 if cfg.Get(k) != v { 327 return err 328 } 329 if cfg.GetInt(k) != v { 330 return err 331 } 332 if cfg.GetString(k) != vs { 333 return err 334 } 335 if !cfg.IsSet(k) { 336 return err 337 } 338 return nil 339 } 340 341 for i := 0; i < 20; i++ { 342 i := i 343 r.Run(func() error { 344 const v = 42 345 k := fmt.Sprintf("k%d", i) 346 if err := setAndGet(k, v); err != nil { 347 return err 348 } 349 350 m := maps.Params{ 351 "new": 42, 352 } 353 354 cfg.Merge("", m) 355 356 return nil 357 }) 358 } 359 360 c.Assert(r.Wait(), qt.IsNil) 361 }) 362 } 363 364 func BenchmarkDefaultConfigProvider(b *testing.B) { 365 type cfger interface { 366 Get(key string) interface{} 367 Set(key string, value interface{}) 368 IsSet(key string) bool 369 } 370 371 newMap := func() map[string]interface{} { 372 return map[string]interface{}{ 373 "a": map[string]interface{}{ 374 "b": map[string]interface{}{ 375 "c": 32, 376 "d": 43, 377 }, 378 }, 379 "b": 62, 380 } 381 } 382 383 runMethods := func(b *testing.B, cfg cfger) { 384 m := newMap() 385 cfg.Set("mymap", m) 386 cfg.Set("num", 32) 387 if !(cfg.IsSet("mymap") && cfg.IsSet("mymap.a") && cfg.IsSet("mymap.a.b") && cfg.IsSet("mymap.a.b.c")) { 388 b.Fatal("IsSet failed") 389 } 390 391 if cfg.Get("num") != 32 { 392 b.Fatal("Get failed") 393 } 394 395 if cfg.Get("mymap.a.b.c") != 32 { 396 b.Fatal("Get failed") 397 } 398 } 399 400 b.Run("Viper", func(b *testing.B) { 401 v := viper.New() 402 for i := 0; i < b.N; i++ { 403 runMethods(b, v) 404 } 405 }) 406 407 b.Run("Custom", func(b *testing.B) { 408 cfg := New() 409 for i := 0; i < b.N; i++ { 410 runMethods(b, cfg) 411 } 412 }) 413 }