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