github.com/lixiangzhong/viper@v1.2.2-0.20181106052354-0c5e9e53bcb8/viper_test.go (about) 1 // Copyright © 2014 Steve Francia <spf@spf13.com>. 2 // 3 // Use of this source code is governed by an MIT-style 4 // license that can be found in the LICENSE file. 5 6 package viper 7 8 import ( 9 "bytes" 10 "encoding/json" 11 "fmt" 12 "io" 13 "io/ioutil" 14 "os" 15 "os/exec" 16 "path" 17 "reflect" 18 "runtime" 19 "sort" 20 "strings" 21 "sync" 22 "testing" 23 "time" 24 25 "github.com/fsnotify/fsnotify" 26 "github.com/mitchellh/mapstructure" 27 "github.com/spf13/afero" 28 "github.com/spf13/cast" 29 30 "github.com/spf13/pflag" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 ) 34 35 var yamlExample = []byte(`Hacker: true 36 name: steve 37 hobbies: 38 - skateboarding 39 - snowboarding 40 - go 41 clothing: 42 jacket: leather 43 trousers: denim 44 pants: 45 size: large 46 age: 35 47 eyes : brown 48 beard: true 49 `) 50 51 var yamlExampleWithExtras = []byte(`Existing: true 52 Bogus: true 53 `) 54 55 type testUnmarshalExtra struct { 56 Existing bool 57 } 58 59 var tomlExample = []byte(` 60 title = "TOML Example" 61 62 [owner] 63 organization = "MongoDB" 64 Bio = "MongoDB Chief Developer Advocate & Hacker at Large" 65 dob = 1979-05-27T07:32:00Z # First class dates? Why not?`) 66 67 var jsonExample = []byte(`{ 68 "id": "0001", 69 "type": "donut", 70 "name": "Cake", 71 "ppu": 0.55, 72 "batters": { 73 "batter": [ 74 { "type": "Regular" }, 75 { "type": "Chocolate" }, 76 { "type": "Blueberry" }, 77 { "type": "Devil's Food" } 78 ] 79 } 80 }`) 81 82 var hclExample = []byte(` 83 id = "0001" 84 type = "donut" 85 name = "Cake" 86 ppu = 0.55 87 foos { 88 foo { 89 key = 1 90 } 91 foo { 92 key = 2 93 } 94 foo { 95 key = 3 96 } 97 foo { 98 key = 4 99 } 100 }`) 101 102 var propertiesExample = []byte(` 103 p_id: 0001 104 p_type: donut 105 p_name: Cake 106 p_ppu: 0.55 107 p_batters.batter.type: Regular 108 `) 109 110 var remoteExample = []byte(`{ 111 "id":"0002", 112 "type":"cronut", 113 "newkey":"remote" 114 }`) 115 116 func initConfigs() { 117 Reset() 118 var r io.Reader 119 SetConfigType("yaml") 120 r = bytes.NewReader(yamlExample) 121 unmarshalReader(r, v.config) 122 123 SetConfigType("json") 124 r = bytes.NewReader(jsonExample) 125 unmarshalReader(r, v.config) 126 127 SetConfigType("hcl") 128 r = bytes.NewReader(hclExample) 129 unmarshalReader(r, v.config) 130 131 SetConfigType("properties") 132 r = bytes.NewReader(propertiesExample) 133 unmarshalReader(r, v.config) 134 135 SetConfigType("toml") 136 r = bytes.NewReader(tomlExample) 137 unmarshalReader(r, v.config) 138 139 SetConfigType("json") 140 remote := bytes.NewReader(remoteExample) 141 unmarshalReader(remote, v.kvstore) 142 } 143 144 func initConfig(typ, config string) { 145 Reset() 146 SetConfigType(typ) 147 r := strings.NewReader(config) 148 149 if err := unmarshalReader(r, v.config); err != nil { 150 panic(err) 151 } 152 } 153 154 func initYAML() { 155 initConfig("yaml", string(yamlExample)) 156 } 157 158 func initJSON() { 159 Reset() 160 SetConfigType("json") 161 r := bytes.NewReader(jsonExample) 162 163 unmarshalReader(r, v.config) 164 } 165 166 func initProperties() { 167 Reset() 168 SetConfigType("properties") 169 r := bytes.NewReader(propertiesExample) 170 171 unmarshalReader(r, v.config) 172 } 173 174 func initTOML() { 175 Reset() 176 SetConfigType("toml") 177 r := bytes.NewReader(tomlExample) 178 179 unmarshalReader(r, v.config) 180 } 181 182 func initHcl() { 183 Reset() 184 SetConfigType("hcl") 185 r := bytes.NewReader(hclExample) 186 187 unmarshalReader(r, v.config) 188 } 189 190 // make directories for testing 191 func initDirs(t *testing.T) (string, string, func()) { 192 193 var ( 194 testDirs = []string{`a a`, `b`, `c\c`, `D_`} 195 config = `improbable` 196 ) 197 198 root, err := ioutil.TempDir("", "") 199 200 cleanup := true 201 defer func() { 202 if cleanup { 203 os.Chdir("..") 204 os.RemoveAll(root) 205 } 206 }() 207 208 assert.Nil(t, err) 209 210 err = os.Chdir(root) 211 assert.Nil(t, err) 212 213 for _, dir := range testDirs { 214 err = os.Mkdir(dir, 0750) 215 assert.Nil(t, err) 216 217 err = ioutil.WriteFile( 218 path.Join(dir, config+".toml"), 219 []byte("key = \"value is "+dir+"\"\n"), 220 0640) 221 assert.Nil(t, err) 222 } 223 224 cleanup = false 225 return root, config, func() { 226 os.Chdir("..") 227 os.RemoveAll(root) 228 } 229 } 230 231 //stubs for PFlag Values 232 type stringValue string 233 234 func newStringValue(val string, p *string) *stringValue { 235 *p = val 236 return (*stringValue)(p) 237 } 238 239 func (s *stringValue) Set(val string) error { 240 *s = stringValue(val) 241 return nil 242 } 243 244 func (s *stringValue) Type() string { 245 return "string" 246 } 247 248 func (s *stringValue) String() string { 249 return fmt.Sprintf("%s", *s) 250 } 251 252 func TestBasics(t *testing.T) { 253 SetConfigFile("/tmp/config.yaml") 254 filename, err := v.getConfigFile() 255 assert.Equal(t, "/tmp/config.yaml", filename) 256 assert.NoError(t, err) 257 } 258 259 func TestDefault(t *testing.T) { 260 SetDefault("age", 45) 261 assert.Equal(t, 45, Get("age")) 262 263 SetDefault("clothing.jacket", "slacks") 264 assert.Equal(t, "slacks", Get("clothing.jacket")) 265 266 SetConfigType("yaml") 267 err := ReadConfig(bytes.NewBuffer(yamlExample)) 268 269 assert.NoError(t, err) 270 assert.Equal(t, "leather", Get("clothing.jacket")) 271 } 272 273 func TestUnmarshaling(t *testing.T) { 274 SetConfigType("yaml") 275 r := bytes.NewReader(yamlExample) 276 277 unmarshalReader(r, v.config) 278 assert.True(t, InConfig("name")) 279 assert.False(t, InConfig("state")) 280 assert.Equal(t, "steve", Get("name")) 281 assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies")) 282 assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing")) 283 assert.Equal(t, 35, Get("age")) 284 } 285 286 func TestUnmarshalExact(t *testing.T) { 287 vip := New() 288 target := &testUnmarshalExtra{} 289 vip.SetConfigType("yaml") 290 r := bytes.NewReader(yamlExampleWithExtras) 291 vip.ReadConfig(r) 292 err := vip.UnmarshalExact(target) 293 if err == nil { 294 t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields") 295 } 296 } 297 298 func TestOverrides(t *testing.T) { 299 Set("age", 40) 300 assert.Equal(t, 40, Get("age")) 301 } 302 303 func TestDefaultPost(t *testing.T) { 304 assert.NotEqual(t, "NYC", Get("state")) 305 SetDefault("state", "NYC") 306 assert.Equal(t, "NYC", Get("state")) 307 } 308 309 func TestAliases(t *testing.T) { 310 RegisterAlias("years", "age") 311 assert.Equal(t, 40, Get("years")) 312 Set("years", 45) 313 assert.Equal(t, 45, Get("age")) 314 } 315 316 func TestAliasInConfigFile(t *testing.T) { 317 // the config file specifies "beard". If we make this an alias for 318 // "hasbeard", we still want the old config file to work with beard. 319 RegisterAlias("beard", "hasbeard") 320 assert.Equal(t, true, Get("hasbeard")) 321 Set("hasbeard", false) 322 assert.Equal(t, false, Get("beard")) 323 } 324 325 func TestYML(t *testing.T) { 326 initYAML() 327 assert.Equal(t, "steve", Get("name")) 328 } 329 330 func TestJSON(t *testing.T) { 331 initJSON() 332 assert.Equal(t, "0001", Get("id")) 333 } 334 335 func TestProperties(t *testing.T) { 336 initProperties() 337 assert.Equal(t, "0001", Get("p_id")) 338 } 339 340 func TestTOML(t *testing.T) { 341 initTOML() 342 assert.Equal(t, "TOML Example", Get("title")) 343 } 344 345 func TestHCL(t *testing.T) { 346 initHcl() 347 assert.Equal(t, "0001", Get("id")) 348 assert.Equal(t, 0.55, Get("ppu")) 349 assert.Equal(t, "donut", Get("type")) 350 assert.Equal(t, "Cake", Get("name")) 351 Set("id", "0002") 352 assert.Equal(t, "0002", Get("id")) 353 assert.NotEqual(t, "cronut", Get("type")) 354 } 355 356 func TestRemotePrecedence(t *testing.T) { 357 initJSON() 358 359 remote := bytes.NewReader(remoteExample) 360 assert.Equal(t, "0001", Get("id")) 361 unmarshalReader(remote, v.kvstore) 362 assert.Equal(t, "0001", Get("id")) 363 assert.NotEqual(t, "cronut", Get("type")) 364 assert.Equal(t, "remote", Get("newkey")) 365 Set("newkey", "newvalue") 366 assert.NotEqual(t, "remote", Get("newkey")) 367 assert.Equal(t, "newvalue", Get("newkey")) 368 Set("newkey", "remote") 369 } 370 371 func TestEnv(t *testing.T) { 372 initJSON() 373 374 BindEnv("id") 375 BindEnv("f", "FOOD") 376 377 os.Setenv("ID", "13") 378 os.Setenv("FOOD", "apple") 379 os.Setenv("NAME", "crunk") 380 381 assert.Equal(t, "13", Get("id")) 382 assert.Equal(t, "apple", Get("f")) 383 assert.Equal(t, "Cake", Get("name")) 384 385 AutomaticEnv() 386 387 assert.Equal(t, "crunk", Get("name")) 388 389 } 390 391 func TestEnvPrefix(t *testing.T) { 392 initJSON() 393 394 SetEnvPrefix("foo") // will be uppercased automatically 395 BindEnv("id") 396 BindEnv("f", "FOOD") // not using prefix 397 398 os.Setenv("FOO_ID", "13") 399 os.Setenv("FOOD", "apple") 400 os.Setenv("FOO_NAME", "crunk") 401 402 assert.Equal(t, "13", Get("id")) 403 assert.Equal(t, "apple", Get("f")) 404 assert.Equal(t, "Cake", Get("name")) 405 406 AutomaticEnv() 407 408 assert.Equal(t, "crunk", Get("name")) 409 } 410 411 func TestAutoEnv(t *testing.T) { 412 Reset() 413 414 AutomaticEnv() 415 os.Setenv("FOO_BAR", "13") 416 assert.Equal(t, "13", Get("foo_bar")) 417 } 418 419 func TestAutoEnvWithPrefix(t *testing.T) { 420 Reset() 421 422 AutomaticEnv() 423 SetEnvPrefix("Baz") 424 os.Setenv("BAZ_BAR", "13") 425 assert.Equal(t, "13", Get("bar")) 426 } 427 428 func TestSetEnvKeyReplacer(t *testing.T) { 429 Reset() 430 431 AutomaticEnv() 432 os.Setenv("REFRESH_INTERVAL", "30s") 433 434 replacer := strings.NewReplacer("-", "_") 435 SetEnvKeyReplacer(replacer) 436 437 assert.Equal(t, "30s", Get("refresh-interval")) 438 } 439 440 func TestAllKeys(t *testing.T) { 441 initConfigs() 442 443 ks := sort.StringSlice{"title", "newkey", "owner.organization", "owner.dob", "owner.bio", "name", "beard", "ppu", "batters.batter", "hobbies", "clothing.jacket", "clothing.trousers", "clothing.pants.size", "age", "hacker", "id", "type", "eyes", "p_id", "p_ppu", "p_batters.batter.type", "p_type", "p_name", "foos"} 444 dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") 445 all := map[string]interface{}{"owner": map[string]interface{}{"organization": "MongoDB", "bio": "MongoDB Chief Developer Advocate & Hacker at Large", "dob": dob}, "title": "TOML Example", "ppu": 0.55, "eyes": "brown", "clothing": map[string]interface{}{"trousers": "denim", "jacket": "leather", "pants": map[string]interface{}{"size": "large"}}, "id": "0001", "batters": map[string]interface{}{"batter": []interface{}{map[string]interface{}{"type": "Regular"}, map[string]interface{}{"type": "Chocolate"}, map[string]interface{}{"type": "Blueberry"}, map[string]interface{}{"type": "Devil's Food"}}}, "hacker": true, "beard": true, "hobbies": []interface{}{"skateboarding", "snowboarding", "go"}, "age": 35, "type": "donut", "newkey": "remote", "name": "Cake", "p_id": "0001", "p_ppu": "0.55", "p_name": "Cake", "p_batters": map[string]interface{}{"batter": map[string]interface{}{"type": "Regular"}}, "p_type": "donut", "foos": []map[string]interface{}{map[string]interface{}{"foo": []map[string]interface{}{map[string]interface{}{"key": 1}, map[string]interface{}{"key": 2}, map[string]interface{}{"key": 3}, map[string]interface{}{"key": 4}}}}} 446 447 var allkeys sort.StringSlice 448 allkeys = AllKeys() 449 allkeys.Sort() 450 ks.Sort() 451 452 assert.Equal(t, ks, allkeys) 453 assert.Equal(t, all, AllSettings()) 454 } 455 456 func TestAllKeysWithEnv(t *testing.T) { 457 v := New() 458 459 // bind and define environment variables (including a nested one) 460 v.BindEnv("id") 461 v.BindEnv("foo.bar") 462 v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) 463 os.Setenv("ID", "13") 464 os.Setenv("FOO_BAR", "baz") 465 466 expectedKeys := sort.StringSlice{"id", "foo.bar"} 467 expectedKeys.Sort() 468 keys := sort.StringSlice(v.AllKeys()) 469 keys.Sort() 470 assert.Equal(t, expectedKeys, keys) 471 } 472 473 func TestAliasesOfAliases(t *testing.T) { 474 Set("Title", "Checking Case") 475 RegisterAlias("Foo", "Bar") 476 RegisterAlias("Bar", "Title") 477 assert.Equal(t, "Checking Case", Get("FOO")) 478 } 479 480 func TestRecursiveAliases(t *testing.T) { 481 RegisterAlias("Baz", "Roo") 482 RegisterAlias("Roo", "baz") 483 } 484 485 func TestUnmarshal(t *testing.T) { 486 SetDefault("port", 1313) 487 Set("name", "Steve") 488 Set("duration", "1s1ms") 489 490 type config struct { 491 Port int 492 Name string 493 Duration time.Duration 494 } 495 496 var C config 497 498 err := Unmarshal(&C) 499 if err != nil { 500 t.Fatalf("unable to decode into struct, %v", err) 501 } 502 503 assert.Equal(t, &config{Name: "Steve", Port: 1313, Duration: time.Second + time.Millisecond}, &C) 504 505 Set("port", 1234) 506 err = Unmarshal(&C) 507 if err != nil { 508 t.Fatalf("unable to decode into struct, %v", err) 509 } 510 assert.Equal(t, &config{Name: "Steve", Port: 1234, Duration: time.Second + time.Millisecond}, &C) 511 } 512 513 func TestUnmarshalWithDecoderOptions(t *testing.T) { 514 Set("credentials", "{\"foo\":\"bar\"}") 515 516 opt := DecodeHook(mapstructure.ComposeDecodeHookFunc( 517 mapstructure.StringToTimeDurationHookFunc(), 518 mapstructure.StringToSliceHookFunc(","), 519 // Custom Decode Hook Function 520 func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) { 521 if rf != reflect.String || rt != reflect.Map { 522 return data, nil 523 } 524 m := map[string]string{} 525 raw := data.(string) 526 if raw == "" { 527 return m, nil 528 } 529 return m, json.Unmarshal([]byte(raw), &m) 530 }, 531 )) 532 533 type config struct { 534 Credentials map[string]string 535 } 536 537 var C config 538 539 err := Unmarshal(&C, opt) 540 if err != nil { 541 t.Fatalf("unable to decode into struct, %v", err) 542 } 543 544 assert.Equal(t, &config{ 545 Credentials: map[string]string{"foo": "bar"}, 546 }, &C) 547 } 548 549 func TestBindPFlags(t *testing.T) { 550 v := New() // create independent Viper object 551 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) 552 553 var testValues = map[string]*string{ 554 "host": nil, 555 "port": nil, 556 "endpoint": nil, 557 } 558 559 var mutatedTestValues = map[string]string{ 560 "host": "localhost", 561 "port": "6060", 562 "endpoint": "/public", 563 } 564 565 for name := range testValues { 566 testValues[name] = flagSet.String(name, "", "test") 567 } 568 569 err := v.BindPFlags(flagSet) 570 if err != nil { 571 t.Fatalf("error binding flag set, %v", err) 572 } 573 574 flagSet.VisitAll(func(flag *pflag.Flag) { 575 flag.Value.Set(mutatedTestValues[flag.Name]) 576 flag.Changed = true 577 }) 578 579 for name, expected := range mutatedTestValues { 580 assert.Equal(t, expected, v.Get(name)) 581 } 582 583 } 584 585 func TestBindPFlagsStringSlice(t *testing.T) { 586 for _, testValue := range []struct { 587 Expected []string 588 Value string 589 }{ 590 {[]string{}, ""}, 591 {[]string{"jeden"}, "jeden"}, 592 {[]string{"dwa", "trzy"}, "dwa,trzy"}, 593 {[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""}} { 594 595 for _, changed := range []bool{true, false} { 596 v := New() // create independent Viper object 597 flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) 598 flagSet.StringSlice("stringslice", testValue.Expected, "test") 599 flagSet.Visit(func(f *pflag.Flag) { 600 if len(testValue.Value) > 0 { 601 f.Value.Set(testValue.Value) 602 f.Changed = changed 603 } 604 }) 605 606 err := v.BindPFlags(flagSet) 607 if err != nil { 608 t.Fatalf("error binding flag set, %v", err) 609 } 610 611 type TestStr struct { 612 StringSlice []string 613 } 614 val := &TestStr{} 615 if err := v.Unmarshal(val); err != nil { 616 t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err) 617 } 618 assert.Equal(t, testValue.Expected, val.StringSlice) 619 } 620 } 621 } 622 623 func TestBindPFlag(t *testing.T) { 624 var testString = "testing" 625 var testValue = newStringValue(testString, &testString) 626 627 flag := &pflag.Flag{ 628 Name: "testflag", 629 Value: testValue, 630 Changed: false, 631 } 632 633 BindPFlag("testvalue", flag) 634 635 assert.Equal(t, testString, Get("testvalue")) 636 637 flag.Value.Set("testing_mutate") 638 flag.Changed = true //hack for pflag usage 639 640 assert.Equal(t, "testing_mutate", Get("testvalue")) 641 642 } 643 644 func TestBoundCaseSensitivity(t *testing.T) { 645 assert.Equal(t, "brown", Get("eyes")) 646 647 BindEnv("eYEs", "TURTLE_EYES") 648 os.Setenv("TURTLE_EYES", "blue") 649 650 assert.Equal(t, "blue", Get("eyes")) 651 652 var testString = "green" 653 var testValue = newStringValue(testString, &testString) 654 655 flag := &pflag.Flag{ 656 Name: "eyeballs", 657 Value: testValue, 658 Changed: true, 659 } 660 661 BindPFlag("eYEs", flag) 662 assert.Equal(t, "green", Get("eyes")) 663 664 } 665 666 func TestSizeInBytes(t *testing.T) { 667 input := map[string]uint{ 668 "": 0, 669 "b": 0, 670 "12 bytes": 0, 671 "200000000000gb": 0, 672 "12 b": 12, 673 "43 MB": 43 * (1 << 20), 674 "10mb": 10 * (1 << 20), 675 "1gb": 1 << 30, 676 } 677 678 for str, expected := range input { 679 assert.Equal(t, expected, parseSizeInBytes(str), str) 680 } 681 } 682 683 func TestFindsNestedKeys(t *testing.T) { 684 initConfigs() 685 dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") 686 687 Set("super", map[string]interface{}{ 688 "deep": map[string]interface{}{ 689 "nested": "value", 690 }, 691 }) 692 693 expected := map[string]interface{}{ 694 "super": map[string]interface{}{ 695 "deep": map[string]interface{}{ 696 "nested": "value", 697 }, 698 }, 699 "super.deep": map[string]interface{}{ 700 "nested": "value", 701 }, 702 "super.deep.nested": "value", 703 "owner.organization": "MongoDB", 704 "batters.batter": []interface{}{ 705 map[string]interface{}{ 706 "type": "Regular", 707 }, 708 map[string]interface{}{ 709 "type": "Chocolate", 710 }, 711 map[string]interface{}{ 712 "type": "Blueberry", 713 }, 714 map[string]interface{}{ 715 "type": "Devil's Food", 716 }, 717 }, 718 "hobbies": []interface{}{ 719 "skateboarding", "snowboarding", "go", 720 }, 721 "title": "TOML Example", 722 "newkey": "remote", 723 "batters": map[string]interface{}{ 724 "batter": []interface{}{ 725 map[string]interface{}{ 726 "type": "Regular", 727 }, 728 map[string]interface{}{ 729 "type": "Chocolate", 730 }, map[string]interface{}{ 731 "type": "Blueberry", 732 }, map[string]interface{}{ 733 "type": "Devil's Food", 734 }, 735 }, 736 }, 737 "eyes": "brown", 738 "age": 35, 739 "owner": map[string]interface{}{ 740 "organization": "MongoDB", 741 "bio": "MongoDB Chief Developer Advocate & Hacker at Large", 742 "dob": dob, 743 }, 744 "owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large", 745 "type": "donut", 746 "id": "0001", 747 "name": "Cake", 748 "hacker": true, 749 "ppu": 0.55, 750 "clothing": map[string]interface{}{ 751 "jacket": "leather", 752 "trousers": "denim", 753 "pants": map[string]interface{}{ 754 "size": "large", 755 }, 756 }, 757 "clothing.jacket": "leather", 758 "clothing.pants.size": "large", 759 "clothing.trousers": "denim", 760 "owner.dob": dob, 761 "beard": true, 762 "foos": []map[string]interface{}{ 763 map[string]interface{}{ 764 "foo": []map[string]interface{}{ 765 map[string]interface{}{ 766 "key": 1, 767 }, 768 map[string]interface{}{ 769 "key": 2, 770 }, 771 map[string]interface{}{ 772 "key": 3, 773 }, 774 map[string]interface{}{ 775 "key": 4, 776 }, 777 }, 778 }, 779 }, 780 } 781 782 for key, expectedValue := range expected { 783 784 assert.Equal(t, expectedValue, v.Get(key)) 785 } 786 787 } 788 789 func TestReadBufConfig(t *testing.T) { 790 v := New() 791 v.SetConfigType("yaml") 792 v.ReadConfig(bytes.NewBuffer(yamlExample)) 793 t.Log(v.AllKeys()) 794 795 assert.True(t, v.InConfig("name")) 796 assert.False(t, v.InConfig("state")) 797 assert.Equal(t, "steve", v.Get("name")) 798 assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies")) 799 assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing")) 800 assert.Equal(t, 35, v.Get("age")) 801 } 802 803 func TestIsSet(t *testing.T) { 804 v := New() 805 v.SetConfigType("yaml") 806 v.ReadConfig(bytes.NewBuffer(yamlExample)) 807 assert.True(t, v.IsSet("clothing.jacket")) 808 assert.False(t, v.IsSet("clothing.jackets")) 809 assert.False(t, v.IsSet("helloworld")) 810 v.Set("helloworld", "fubar") 811 assert.True(t, v.IsSet("helloworld")) 812 } 813 814 func TestDirsSearch(t *testing.T) { 815 816 root, config, cleanup := initDirs(t) 817 defer cleanup() 818 819 v := New() 820 v.SetConfigName(config) 821 v.SetDefault(`key`, `default`) 822 823 entries, err := ioutil.ReadDir(root) 824 for _, e := range entries { 825 if e.IsDir() { 826 v.AddConfigPath(e.Name()) 827 } 828 } 829 830 err = v.ReadInConfig() 831 assert.Nil(t, err) 832 833 assert.Equal(t, `value is `+path.Base(v.configPaths[0]), v.GetString(`key`)) 834 } 835 836 func TestWrongDirsSearchNotFound(t *testing.T) { 837 838 _, config, cleanup := initDirs(t) 839 defer cleanup() 840 841 v := New() 842 v.SetConfigName(config) 843 v.SetDefault(`key`, `default`) 844 845 v.AddConfigPath(`whattayoutalkingbout`) 846 v.AddConfigPath(`thispathaintthere`) 847 848 err := v.ReadInConfig() 849 assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) 850 851 // Even though config did not load and the error might have 852 // been ignored by the client, the default still loads 853 assert.Equal(t, `default`, v.GetString(`key`)) 854 } 855 856 func TestWrongDirsSearchNotFoundForMerge(t *testing.T) { 857 858 _, config, cleanup := initDirs(t) 859 defer cleanup() 860 861 v := New() 862 v.SetConfigName(config) 863 v.SetDefault(`key`, `default`) 864 865 v.AddConfigPath(`whattayoutalkingbout`) 866 v.AddConfigPath(`thispathaintthere`) 867 868 err := v.MergeInConfig() 869 assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err)) 870 871 // Even though config did not load and the error might have 872 // been ignored by the client, the default still loads 873 assert.Equal(t, `default`, v.GetString(`key`)) 874 } 875 876 func TestSub(t *testing.T) { 877 v := New() 878 v.SetConfigType("yaml") 879 v.ReadConfig(bytes.NewBuffer(yamlExample)) 880 881 subv := v.Sub("clothing") 882 assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size")) 883 884 subv = v.Sub("clothing.pants") 885 assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size")) 886 887 subv = v.Sub("clothing.pants.size") 888 assert.Equal(t, (*Viper)(nil), subv) 889 890 subv = v.Sub("missing.key") 891 assert.Equal(t, (*Viper)(nil), subv) 892 } 893 894 var hclWriteExpected = []byte(`"foos" = { 895 "foo" = { 896 "key" = 1 897 } 898 899 "foo" = { 900 "key" = 2 901 } 902 903 "foo" = { 904 "key" = 3 905 } 906 907 "foo" = { 908 "key" = 4 909 } 910 } 911 912 "id" = "0001" 913 914 "name" = "Cake" 915 916 "ppu" = 0.55 917 918 "type" = "donut"`) 919 920 func TestWriteConfigHCL(t *testing.T) { 921 v := New() 922 fs := afero.NewMemMapFs() 923 v.SetFs(fs) 924 v.SetConfigName("c") 925 v.SetConfigType("hcl") 926 err := v.ReadConfig(bytes.NewBuffer(hclExample)) 927 if err != nil { 928 t.Fatal(err) 929 } 930 if err := v.WriteConfigAs("c.hcl"); err != nil { 931 t.Fatal(err) 932 } 933 read, err := afero.ReadFile(fs, "c.hcl") 934 if err != nil { 935 t.Fatal(err) 936 } 937 assert.Equal(t, hclWriteExpected, read) 938 } 939 940 var jsonWriteExpected = []byte(`{ 941 "batters": { 942 "batter": [ 943 { 944 "type": "Regular" 945 }, 946 { 947 "type": "Chocolate" 948 }, 949 { 950 "type": "Blueberry" 951 }, 952 { 953 "type": "Devil's Food" 954 } 955 ] 956 }, 957 "id": "0001", 958 "name": "Cake", 959 "ppu": 0.55, 960 "type": "donut" 961 }`) 962 963 func TestWriteConfigJson(t *testing.T) { 964 v := New() 965 fs := afero.NewMemMapFs() 966 v.SetFs(fs) 967 v.SetConfigName("c") 968 v.SetConfigType("json") 969 err := v.ReadConfig(bytes.NewBuffer(jsonExample)) 970 if err != nil { 971 t.Fatal(err) 972 } 973 if err := v.WriteConfigAs("c.json"); err != nil { 974 t.Fatal(err) 975 } 976 read, err := afero.ReadFile(fs, "c.json") 977 if err != nil { 978 t.Fatal(err) 979 } 980 assert.Equal(t, jsonWriteExpected, read) 981 } 982 983 var propertiesWriteExpected = []byte(`p_id = 0001 984 p_type = donut 985 p_name = Cake 986 p_ppu = 0.55 987 p_batters.batter.type = Regular 988 `) 989 990 func TestWriteConfigProperties(t *testing.T) { 991 v := New() 992 fs := afero.NewMemMapFs() 993 v.SetFs(fs) 994 v.SetConfigName("c") 995 v.SetConfigType("properties") 996 err := v.ReadConfig(bytes.NewBuffer(propertiesExample)) 997 if err != nil { 998 t.Fatal(err) 999 } 1000 if err := v.WriteConfigAs("c.properties"); err != nil { 1001 t.Fatal(err) 1002 } 1003 read, err := afero.ReadFile(fs, "c.properties") 1004 if err != nil { 1005 t.Fatal(err) 1006 } 1007 assert.Equal(t, propertiesWriteExpected, read) 1008 } 1009 1010 func TestWriteConfigTOML(t *testing.T) { 1011 fs := afero.NewMemMapFs() 1012 v := New() 1013 v.SetFs(fs) 1014 v.SetConfigName("c") 1015 v.SetConfigType("toml") 1016 err := v.ReadConfig(bytes.NewBuffer(tomlExample)) 1017 if err != nil { 1018 t.Fatal(err) 1019 } 1020 if err := v.WriteConfigAs("c.toml"); err != nil { 1021 t.Fatal(err) 1022 } 1023 1024 // The TOML String method does not order the contents. 1025 // Therefore, we must read the generated file and compare the data. 1026 v2 := New() 1027 v2.SetFs(fs) 1028 v2.SetConfigName("c") 1029 v2.SetConfigType("toml") 1030 v2.SetConfigFile("c.toml") 1031 err = v2.ReadInConfig() 1032 if err != nil { 1033 t.Fatal(err) 1034 } 1035 1036 assert.Equal(t, v.GetString("title"), v2.GetString("title")) 1037 assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio")) 1038 assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob")) 1039 assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization")) 1040 } 1041 1042 var yamlWriteExpected = []byte(`age: 35 1043 beard: true 1044 clothing: 1045 jacket: leather 1046 pants: 1047 size: large 1048 trousers: denim 1049 eyes: brown 1050 hacker: true 1051 hobbies: 1052 - skateboarding 1053 - snowboarding 1054 - go 1055 name: steve 1056 `) 1057 1058 func TestWriteConfigYAML(t *testing.T) { 1059 v := New() 1060 fs := afero.NewMemMapFs() 1061 v.SetFs(fs) 1062 v.SetConfigName("c") 1063 v.SetConfigType("yaml") 1064 err := v.ReadConfig(bytes.NewBuffer(yamlExample)) 1065 if err != nil { 1066 t.Fatal(err) 1067 } 1068 if err := v.WriteConfigAs("c.yaml"); err != nil { 1069 t.Fatal(err) 1070 } 1071 read, err := afero.ReadFile(fs, "c.yaml") 1072 if err != nil { 1073 t.Fatal(err) 1074 } 1075 assert.Equal(t, yamlWriteExpected, read) 1076 } 1077 1078 var yamlMergeExampleTgt = []byte(` 1079 hello: 1080 pop: 37890 1081 lagrenum: 765432101234567 1082 world: 1083 - us 1084 - uk 1085 - fr 1086 - de 1087 `) 1088 1089 var yamlMergeExampleSrc = []byte(` 1090 hello: 1091 pop: 45000 1092 lagrenum: 7654321001234567 1093 universe: 1094 - mw 1095 - ad 1096 fu: bar 1097 `) 1098 1099 func TestMergeConfig(t *testing.T) { 1100 v := New() 1101 v.SetConfigType("yml") 1102 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 1103 t.Fatal(err) 1104 } 1105 1106 if pop := v.GetInt("hello.pop"); pop != 37890 { 1107 t.Fatalf("pop != 37890, = %d", pop) 1108 } 1109 1110 if pop := v.GetInt32("hello.pop"); pop != int32(37890) { 1111 t.Fatalf("pop != 37890, = %d", pop) 1112 } 1113 1114 if pop := v.GetInt64("hello.lagrenum"); pop != int64(765432101234567) { 1115 t.Fatalf("int64 lagrenum != 765432101234567, = %d", pop) 1116 } 1117 1118 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1119 t.Fatalf("len(world) != 4, = %d", len(world)) 1120 } 1121 1122 if fu := v.GetString("fu"); fu != "" { 1123 t.Fatalf("fu != \"\", = %s", fu) 1124 } 1125 1126 if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 1127 t.Fatal(err) 1128 } 1129 1130 if pop := v.GetInt("hello.pop"); pop != 45000 { 1131 t.Fatalf("pop != 45000, = %d", pop) 1132 } 1133 1134 if pop := v.GetInt32("hello.pop"); pop != int32(45000) { 1135 t.Fatalf("pop != 45000, = %d", pop) 1136 } 1137 1138 if pop := v.GetInt64("hello.lagrenum"); pop != int64(7654321001234567) { 1139 t.Fatalf("int64 lagrenum != 7654321001234567, = %d", pop) 1140 } 1141 1142 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1143 t.Fatalf("len(world) != 4, = %d", len(world)) 1144 } 1145 1146 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 1147 t.Fatalf("len(universe) != 2, = %d", len(universe)) 1148 } 1149 1150 if fu := v.GetString("fu"); fu != "bar" { 1151 t.Fatalf("fu != \"bar\", = %s", fu) 1152 } 1153 } 1154 1155 func TestMergeConfigNoMerge(t *testing.T) { 1156 v := New() 1157 v.SetConfigType("yml") 1158 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil { 1159 t.Fatal(err) 1160 } 1161 1162 if pop := v.GetInt("hello.pop"); pop != 37890 { 1163 t.Fatalf("pop != 37890, = %d", pop) 1164 } 1165 1166 if world := v.GetStringSlice("hello.world"); len(world) != 4 { 1167 t.Fatalf("len(world) != 4, = %d", len(world)) 1168 } 1169 1170 if fu := v.GetString("fu"); fu != "" { 1171 t.Fatalf("fu != \"\", = %s", fu) 1172 } 1173 1174 if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil { 1175 t.Fatal(err) 1176 } 1177 1178 if pop := v.GetInt("hello.pop"); pop != 45000 { 1179 t.Fatalf("pop != 45000, = %d", pop) 1180 } 1181 1182 if world := v.GetStringSlice("hello.world"); len(world) != 0 { 1183 t.Fatalf("len(world) != 0, = %d", len(world)) 1184 } 1185 1186 if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 { 1187 t.Fatalf("len(universe) != 2, = %d", len(universe)) 1188 } 1189 1190 if fu := v.GetString("fu"); fu != "bar" { 1191 t.Fatalf("fu != \"bar\", = %s", fu) 1192 } 1193 } 1194 1195 func TestUnmarshalingWithAliases(t *testing.T) { 1196 v := New() 1197 v.SetDefault("ID", 1) 1198 v.Set("name", "Steve") 1199 v.Set("lastname", "Owen") 1200 1201 v.RegisterAlias("UserID", "ID") 1202 v.RegisterAlias("Firstname", "name") 1203 v.RegisterAlias("Surname", "lastname") 1204 1205 type config struct { 1206 ID int 1207 FirstName string 1208 Surname string 1209 } 1210 1211 var C config 1212 err := v.Unmarshal(&C) 1213 if err != nil { 1214 t.Fatalf("unable to decode into struct, %v", err) 1215 } 1216 1217 assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C) 1218 } 1219 1220 func TestSetConfigNameClearsFileCache(t *testing.T) { 1221 SetConfigFile("/tmp/config.yaml") 1222 SetConfigName("default") 1223 f, err := v.getConfigFile() 1224 if err == nil { 1225 t.Fatalf("config file cache should have been cleared") 1226 } 1227 assert.Empty(t, f) 1228 } 1229 1230 func TestShadowedNestedValue(t *testing.T) { 1231 1232 config := `name: steve 1233 clothing: 1234 jacket: leather 1235 trousers: denim 1236 pants: 1237 size: large 1238 ` 1239 initConfig("yaml", config) 1240 1241 assert.Equal(t, "steve", GetString("name")) 1242 1243 polyester := "polyester" 1244 SetDefault("clothing.shirt", polyester) 1245 SetDefault("clothing.jacket.price", 100) 1246 1247 assert.Equal(t, "leather", GetString("clothing.jacket")) 1248 assert.Nil(t, Get("clothing.jacket.price")) 1249 assert.Equal(t, polyester, GetString("clothing.shirt")) 1250 1251 clothingSettings := AllSettings()["clothing"].(map[string]interface{}) 1252 assert.Equal(t, "leather", clothingSettings["jacket"]) 1253 assert.Equal(t, polyester, clothingSettings["shirt"]) 1254 } 1255 1256 func TestDotParameter(t *testing.T) { 1257 initJSON() 1258 // shoud take precedence over batters defined in jsonExample 1259 r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`)) 1260 unmarshalReader(r, v.config) 1261 1262 actual := Get("batters.batter") 1263 expected := []interface{}{map[string]interface{}{"type": "Small"}} 1264 assert.Equal(t, expected, actual) 1265 } 1266 1267 func TestCaseInsensitive(t *testing.T) { 1268 for _, config := range []struct { 1269 typ string 1270 content string 1271 }{ 1272 {"yaml", ` 1273 aBcD: 1 1274 eF: 1275 gH: 2 1276 iJk: 3 1277 Lm: 1278 nO: 4 1279 P: 1280 Q: 5 1281 R: 6 1282 `}, 1283 {"json", `{ 1284 "aBcD": 1, 1285 "eF": { 1286 "iJk": 3, 1287 "Lm": { 1288 "P": { 1289 "Q": 5, 1290 "R": 6 1291 }, 1292 "nO": 4 1293 }, 1294 "gH": 2 1295 } 1296 }`}, 1297 {"toml", `aBcD = 1 1298 [eF] 1299 gH = 2 1300 iJk = 3 1301 [eF.Lm] 1302 nO = 4 1303 [eF.Lm.P] 1304 Q = 5 1305 R = 6 1306 `}, 1307 } { 1308 doTestCaseInsensitive(t, config.typ, config.content) 1309 } 1310 } 1311 1312 func TestCaseInsensitiveSet(t *testing.T) { 1313 Reset() 1314 m1 := map[string]interface{}{ 1315 "Foo": 32, 1316 "Bar": map[interface{}]interface { 1317 }{ 1318 "ABc": "A", 1319 "cDE": "B"}, 1320 } 1321 1322 m2 := map[string]interface{}{ 1323 "Foo": 52, 1324 "Bar": map[interface{}]interface { 1325 }{ 1326 "bCd": "A", 1327 "eFG": "B"}, 1328 } 1329 1330 Set("Given1", m1) 1331 Set("Number1", 42) 1332 1333 SetDefault("Given2", m2) 1334 SetDefault("Number2", 52) 1335 1336 // Verify SetDefault 1337 if v := Get("number2"); v != 52 { 1338 t.Fatalf("Expected 52 got %q", v) 1339 } 1340 1341 if v := Get("given2.foo"); v != 52 { 1342 t.Fatalf("Expected 52 got %q", v) 1343 } 1344 1345 if v := Get("given2.bar.bcd"); v != "A" { 1346 t.Fatalf("Expected A got %q", v) 1347 } 1348 1349 if _, ok := m2["Foo"]; !ok { 1350 t.Fatal("Input map changed") 1351 } 1352 1353 // Verify Set 1354 if v := Get("number1"); v != 42 { 1355 t.Fatalf("Expected 42 got %q", v) 1356 } 1357 1358 if v := Get("given1.foo"); v != 32 { 1359 t.Fatalf("Expected 32 got %q", v) 1360 } 1361 1362 if v := Get("given1.bar.abc"); v != "A" { 1363 t.Fatalf("Expected A got %q", v) 1364 } 1365 1366 if _, ok := m1["Foo"]; !ok { 1367 t.Fatal("Input map changed") 1368 } 1369 } 1370 1371 func TestParseNested(t *testing.T) { 1372 type duration struct { 1373 Delay time.Duration 1374 } 1375 1376 type item struct { 1377 Name string 1378 Delay time.Duration 1379 Nested duration 1380 } 1381 1382 config := `[[parent]] 1383 delay="100ms" 1384 [parent.nested] 1385 delay="200ms" 1386 ` 1387 initConfig("toml", config) 1388 1389 var items []item 1390 err := v.UnmarshalKey("parent", &items) 1391 if err != nil { 1392 t.Fatalf("unable to decode into struct, %v", err) 1393 } 1394 1395 assert.Equal(t, 1, len(items)) 1396 assert.Equal(t, 100*time.Millisecond, items[0].Delay) 1397 assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay) 1398 } 1399 1400 func doTestCaseInsensitive(t *testing.T, typ, config string) { 1401 initConfig(typ, config) 1402 Set("RfD", true) 1403 assert.Equal(t, true, Get("rfd")) 1404 assert.Equal(t, true, Get("rFD")) 1405 assert.Equal(t, 1, cast.ToInt(Get("abcd"))) 1406 assert.Equal(t, 1, cast.ToInt(Get("Abcd"))) 1407 assert.Equal(t, 2, cast.ToInt(Get("ef.gh"))) 1408 assert.Equal(t, 3, cast.ToInt(Get("ef.ijk"))) 1409 assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no"))) 1410 assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q"))) 1411 1412 } 1413 1414 func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) { 1415 watchDir, err := ioutil.TempDir("", "") 1416 require.Nil(t, err) 1417 configFile := path.Join(watchDir, "config.yaml") 1418 err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0640) 1419 require.Nil(t, err) 1420 cleanup := func() { 1421 os.RemoveAll(watchDir) 1422 } 1423 v := New() 1424 v.SetConfigFile(configFile) 1425 err = v.ReadInConfig() 1426 require.Nil(t, err) 1427 require.Equal(t, "bar", v.Get("foo")) 1428 return v, configFile, cleanup 1429 } 1430 1431 func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func()) { 1432 watchDir, err := ioutil.TempDir("", "") 1433 require.Nil(t, err) 1434 dataDir1 := path.Join(watchDir, "data1") 1435 err = os.Mkdir(dataDir1, 0777) 1436 require.Nil(t, err) 1437 realConfigFile := path.Join(dataDir1, "config.yaml") 1438 t.Logf("Real config file location: %s\n", realConfigFile) 1439 err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0640) 1440 require.Nil(t, err) 1441 cleanup := func() { 1442 os.RemoveAll(watchDir) 1443 } 1444 // now, symlink the tm `data1` dir to `data` in the baseDir 1445 os.Symlink(dataDir1, path.Join(watchDir, "data")) 1446 // and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml` 1447 configFile := path.Join(watchDir, "config.yaml") 1448 os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile) 1449 t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml")) 1450 // init Viper 1451 v := New() 1452 v.SetConfigFile(configFile) 1453 err = v.ReadInConfig() 1454 require.Nil(t, err) 1455 require.Equal(t, "bar", v.Get("foo")) 1456 return v, watchDir, configFile, cleanup 1457 } 1458 1459 func TestWatchFile(t *testing.T) { 1460 1461 t.Run("file content changed", func(t *testing.T) { 1462 // given a `config.yaml` file being watched 1463 v, configFile, cleanup := newViperWithConfigFile(t) 1464 defer cleanup() 1465 _, err := os.Stat(configFile) 1466 require.NoError(t, err) 1467 t.Logf("test config file: %s\n", configFile) 1468 wg := sync.WaitGroup{} 1469 wg.Add(1) 1470 v.OnConfigChange(func(in fsnotify.Event) { 1471 t.Logf("config file changed") 1472 wg.Done() 1473 }) 1474 v.WatchConfig() 1475 // when overwriting the file and waiting for the custom change notification handler to be triggered 1476 err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0640) 1477 wg.Wait() 1478 // then the config value should have changed 1479 require.Nil(t, err) 1480 assert.Equal(t, "baz", v.Get("foo")) 1481 }) 1482 1483 t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) { 1484 // skip if not executed on Linux 1485 if runtime.GOOS != "linux" { 1486 t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...") 1487 } 1488 v, watchDir, _, _ := newViperWithSymlinkedConfigFile(t) 1489 // defer cleanup() 1490 wg := sync.WaitGroup{} 1491 v.WatchConfig() 1492 v.OnConfigChange(func(in fsnotify.Event) { 1493 t.Logf("config file changed") 1494 wg.Done() 1495 }) 1496 wg.Add(1) 1497 // when link to another `config.yaml` file 1498 dataDir2 := path.Join(watchDir, "data2") 1499 err := os.Mkdir(dataDir2, 0777) 1500 require.Nil(t, err) 1501 configFile2 := path.Join(dataDir2, "config.yaml") 1502 err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0640) 1503 require.Nil(t, err) 1504 // change the symlink using the `ln -sfn` command 1505 err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run() 1506 require.Nil(t, err) 1507 wg.Wait() 1508 // then 1509 require.Nil(t, err) 1510 assert.Equal(t, "baz", v.Get("foo")) 1511 }) 1512 1513 } 1514 1515 func BenchmarkGetBool(b *testing.B) { 1516 key := "BenchmarkGetBool" 1517 v = New() 1518 v.Set(key, true) 1519 1520 for i := 0; i < b.N; i++ { 1521 if !v.GetBool(key) { 1522 b.Fatal("GetBool returned false") 1523 } 1524 } 1525 } 1526 1527 func BenchmarkGet(b *testing.B) { 1528 key := "BenchmarkGet" 1529 v = New() 1530 v.Set(key, true) 1531 1532 for i := 0; i < b.N; i++ { 1533 if !v.Get(key).(bool) { 1534 b.Fatal("Get returned false") 1535 } 1536 } 1537 } 1538 1539 // This is the "perfect result" for the above. 1540 func BenchmarkGetBoolFromMap(b *testing.B) { 1541 m := make(map[string]bool) 1542 key := "BenchmarkGetBool" 1543 m[key] = true 1544 1545 for i := 0; i < b.N; i++ { 1546 if !m[key] { 1547 b.Fatal("Map value was false") 1548 } 1549 } 1550 }