github.com/nsherman-spoton/viper@v1.15.3/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  	"io"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"path"
    16  	"path/filepath"
    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  	"github.com/spf13/pflag"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  
    33  	"github.com/nsherman-spoton/viper/internal/testutil"
    34  )
    35  
    36  // var yamlExample = []byte(`Hacker: true
    37  // name: steve
    38  // hobbies:
    39  //     - skateboarding
    40  //     - snowboarding
    41  //     - go
    42  // clothing:
    43  //     jacket: leather
    44  //     trousers: denim
    45  //     pants:
    46  //         size: large
    47  // age: 35
    48  // eyes : brown
    49  // beard: true
    50  // `)
    51  
    52  var yamlExampleWithExtras = []byte(`Existing: true
    53  Bogus: true
    54  `)
    55  
    56  type testUnmarshalExtra struct {
    57  	Existing bool
    58  }
    59  
    60  var tomlExample = []byte(`
    61  title = "TOML Example"
    62  
    63  [owner]
    64  organization = "MongoDB"
    65  Bio = "MongoDB Chief Developer Advocate & Hacker at Large"
    66  dob = 1979-05-27T07:32:00Z # First class dates? Why not?`)
    67  
    68  var dotenvExample = []byte(`
    69  TITLE_DOTENV="DotEnv Example"
    70  TYPE_DOTENV=donut
    71  NAME_DOTENV=Cake`)
    72  
    73  var jsonExample = []byte(`{
    74  "id": "0001",
    75  "type": "donut",
    76  "name": "Cake",
    77  "ppu": 0.55,
    78  "batters": {
    79          "batter": [
    80                  { "type": "Regular" },
    81                  { "type": "Chocolate" },
    82                  { "type": "Blueberry" },
    83                  { "type": "Devil's Food" }
    84              ]
    85      }
    86  }`)
    87  
    88  var hclExample = []byte(`
    89  id = "0001"
    90  type = "donut"
    91  name = "Cake"
    92  ppu = 0.55
    93  foos {
    94  	foo {
    95  		key = 1
    96  	}
    97  	foo {
    98  		key = 2
    99  	}
   100  	foo {
   101  		key = 3
   102  	}
   103  	foo {
   104  		key = 4
   105  	}
   106  }`)
   107  
   108  var propertiesExample = []byte(`
   109  p_id: 0001
   110  p_type: donut
   111  p_name: Cake
   112  p_ppu: 0.55
   113  p_batters.batter.type: Regular
   114  `)
   115  
   116  var remoteExample = []byte(`{
   117  "id":"0002",
   118  "type":"cronut",
   119  "newkey":"remote"
   120  }`)
   121  
   122  var iniExample = []byte(`; Package name
   123  NAME        = ini
   124  ; Package version
   125  VERSION     = v1
   126  ; Package import path
   127  IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
   128  
   129  # Information about package author
   130  # Bio can be written in multiple lines.
   131  [author]
   132  NAME   = Unknown  ; Succeeding comment
   133  E-MAIL = fake@localhost
   134  GITHUB = https://github.com/%(NAME)s
   135  BIO    = """Gopher.
   136  Coding addict.
   137  Good man.
   138  """  # Succeeding comment`)
   139  
   140  func initConfigs() {
   141  	Reset()
   142  	var r io.Reader
   143  	SetConfigType("yaml")
   144  	r = bytes.NewReader(yamlExample)
   145  	unmarshalReader(r, v.config)
   146  
   147  	SetConfigType("json")
   148  	r = bytes.NewReader(jsonExample)
   149  	unmarshalReader(r, v.config)
   150  
   151  	SetConfigType("hcl")
   152  	r = bytes.NewReader(hclExample)
   153  	unmarshalReader(r, v.config)
   154  
   155  	SetConfigType("properties")
   156  	r = bytes.NewReader(propertiesExample)
   157  	unmarshalReader(r, v.config)
   158  
   159  	SetConfigType("toml")
   160  	r = bytes.NewReader(tomlExample)
   161  	unmarshalReader(r, v.config)
   162  
   163  	SetConfigType("env")
   164  	r = bytes.NewReader(dotenvExample)
   165  	unmarshalReader(r, v.config)
   166  
   167  	SetConfigType("json")
   168  	remote := bytes.NewReader(remoteExample)
   169  	unmarshalReader(remote, v.kvstore)
   170  
   171  	SetConfigType("ini")
   172  	r = bytes.NewReader(iniExample)
   173  	unmarshalReader(r, v.config)
   174  }
   175  
   176  func initConfig(typ, config string) {
   177  	Reset()
   178  	SetConfigType(typ)
   179  	r := strings.NewReader(config)
   180  
   181  	if err := unmarshalReader(r, v.config); err != nil {
   182  		panic(err)
   183  	}
   184  }
   185  
   186  func initYAML() {
   187  	initConfig("yaml", string(yamlExample))
   188  }
   189  
   190  func initJSON() {
   191  	Reset()
   192  	SetConfigType("json")
   193  	r := bytes.NewReader(jsonExample)
   194  
   195  	unmarshalReader(r, v.config)
   196  }
   197  
   198  func initProperties() {
   199  	Reset()
   200  	SetConfigType("properties")
   201  	r := bytes.NewReader(propertiesExample)
   202  
   203  	unmarshalReader(r, v.config)
   204  }
   205  
   206  func initTOML() {
   207  	Reset()
   208  	SetConfigType("toml")
   209  	r := bytes.NewReader(tomlExample)
   210  
   211  	unmarshalReader(r, v.config)
   212  }
   213  
   214  func initDotEnv() {
   215  	Reset()
   216  	SetConfigType("env")
   217  	r := bytes.NewReader(dotenvExample)
   218  
   219  	unmarshalReader(r, v.config)
   220  }
   221  
   222  func initHcl() {
   223  	Reset()
   224  	SetConfigType("hcl")
   225  	r := bytes.NewReader(hclExample)
   226  
   227  	unmarshalReader(r, v.config)
   228  }
   229  
   230  func initIni() {
   231  	Reset()
   232  	SetConfigType("ini")
   233  	r := bytes.NewReader(iniExample)
   234  
   235  	unmarshalReader(r, v.config)
   236  }
   237  
   238  // make directories for testing
   239  func initDirs(t *testing.T) (string, string, func()) {
   240  	var (
   241  		testDirs = []string{`a a`, `b`, `C_`}
   242  		config   = `improbable`
   243  	)
   244  
   245  	if runtime.GOOS != "windows" {
   246  		testDirs = append(testDirs, `d\d`)
   247  	}
   248  
   249  	root, err := ioutil.TempDir("", "")
   250  	require.NoError(t, err, "Failed to create temporary directory")
   251  
   252  	cleanup := true
   253  	defer func() {
   254  		if cleanup {
   255  			os.Chdir("..")
   256  			os.RemoveAll(root)
   257  		}
   258  	}()
   259  
   260  	assert.Nil(t, err)
   261  
   262  	err = os.Chdir(root)
   263  	require.Nil(t, err)
   264  
   265  	for _, dir := range testDirs {
   266  		err = os.Mkdir(dir, 0o750)
   267  		assert.Nil(t, err)
   268  
   269  		err = ioutil.WriteFile(
   270  			path.Join(dir, config+".toml"),
   271  			[]byte("key = \"value is "+dir+"\"\n"),
   272  			0o640)
   273  		assert.Nil(t, err)
   274  	}
   275  
   276  	cleanup = false
   277  	return root, config, func() {
   278  		os.Chdir("..")
   279  		os.RemoveAll(root)
   280  	}
   281  }
   282  
   283  // stubs for PFlag Values
   284  type stringValue string
   285  
   286  func newStringValue(val string, p *string) *stringValue {
   287  	*p = val
   288  	return (*stringValue)(p)
   289  }
   290  
   291  func (s *stringValue) Set(val string) error {
   292  	*s = stringValue(val)
   293  	return nil
   294  }
   295  
   296  func (s *stringValue) Type() string {
   297  	return "string"
   298  }
   299  
   300  func (s *stringValue) String() string {
   301  	return string(*s)
   302  }
   303  
   304  func TestGetConfigFile(t *testing.T) {
   305  	t.Run("config file set", func(t *testing.T) {
   306  		fs := afero.NewMemMapFs()
   307  
   308  		err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
   309  		require.NoError(t, err)
   310  
   311  		_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
   312  		require.NoError(t, err)
   313  
   314  		v := New()
   315  
   316  		v.SetFs(fs)
   317  		v.AddConfigPath("/etc/viper")
   318  		v.SetConfigFile(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
   319  
   320  		filename, err := v.getConfigFile()
   321  		assert.Equal(t, testutil.AbsFilePath(t, "/etc/viper/config.yaml"), filename)
   322  		assert.NoError(t, err)
   323  	})
   324  
   325  	t.Run("find file", func(t *testing.T) {
   326  		fs := afero.NewMemMapFs()
   327  
   328  		err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
   329  		require.NoError(t, err)
   330  
   331  		_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
   332  		require.NoError(t, err)
   333  
   334  		v := New()
   335  
   336  		v.SetFs(fs)
   337  		v.AddConfigPath("/etc/viper")
   338  
   339  		filename, err := v.getConfigFile()
   340  		assert.Equal(t, testutil.AbsFilePath(t, "/etc/viper/config.yaml"), filename)
   341  		assert.NoError(t, err)
   342  	})
   343  
   344  	t.Run("find files only", func(t *testing.T) {
   345  		fs := afero.NewMemMapFs()
   346  
   347  		err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/config"), 0o777)
   348  		require.NoError(t, err)
   349  
   350  		_, err = fs.Create(testutil.AbsFilePath(t, "/etc/config/config.yaml"))
   351  		require.NoError(t, err)
   352  
   353  		v := New()
   354  
   355  		v.SetFs(fs)
   356  		v.AddConfigPath("/etc")
   357  		v.AddConfigPath("/etc/config")
   358  
   359  		filename, err := v.getConfigFile()
   360  		assert.Equal(t, testutil.AbsFilePath(t, "/etc/config/config.yaml"), filename)
   361  		assert.NoError(t, err)
   362  	})
   363  
   364  	t.Run("precedence", func(t *testing.T) {
   365  		fs := afero.NewMemMapFs()
   366  
   367  		err := fs.Mkdir(testutil.AbsFilePath(t, "/home/viper"), 0o777)
   368  		require.NoError(t, err)
   369  
   370  		_, err = fs.Create(testutil.AbsFilePath(t, "/home/viper/config.zml"))
   371  		require.NoError(t, err)
   372  
   373  		err = fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
   374  		require.NoError(t, err)
   375  
   376  		_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.bml"))
   377  		require.NoError(t, err)
   378  
   379  		err = fs.Mkdir(testutil.AbsFilePath(t, "/var/viper"), 0o777)
   380  		require.NoError(t, err)
   381  
   382  		_, err = fs.Create(testutil.AbsFilePath(t, "/var/viper/config.yaml"))
   383  		require.NoError(t, err)
   384  
   385  		v := New()
   386  
   387  		v.SetFs(fs)
   388  		v.AddConfigPath("/home/viper")
   389  		v.AddConfigPath("/etc/viper")
   390  		v.AddConfigPath("/var/viper")
   391  
   392  		filename, err := v.getConfigFile()
   393  		assert.Equal(t, testutil.AbsFilePath(t, "/var/viper/config.yaml"), filename)
   394  		assert.NoError(t, err)
   395  	})
   396  
   397  	t.Run("without extension", func(t *testing.T) {
   398  		fs := afero.NewMemMapFs()
   399  
   400  		err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
   401  		require.NoError(t, err)
   402  
   403  		_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/.dotfilenoext"))
   404  		require.NoError(t, err)
   405  
   406  		v := New()
   407  
   408  		v.SetFs(fs)
   409  		v.AddConfigPath("/etc/viper")
   410  		v.SetConfigName(".dotfilenoext")
   411  		v.SetConfigType("yaml")
   412  
   413  		filename, err := v.getConfigFile()
   414  		assert.Equal(t, testutil.AbsFilePath(t, "/etc/viper/.dotfilenoext"), filename)
   415  		assert.NoError(t, err)
   416  	})
   417  
   418  	t.Run("without extension and config type", func(t *testing.T) {
   419  		fs := afero.NewMemMapFs()
   420  
   421  		err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
   422  		require.NoError(t, err)
   423  
   424  		_, err = fs.Create(testutil.AbsFilePath(t, "/etc/viper/.dotfilenoext"))
   425  		require.NoError(t, err)
   426  
   427  		v := New()
   428  
   429  		v.SetFs(fs)
   430  		v.AddConfigPath("/etc/viper")
   431  		v.SetConfigName(".dotfilenoext")
   432  
   433  		_, err = v.getConfigFile()
   434  		// unless config type is set, files without extension
   435  		// are not considered
   436  		assert.Error(t, err)
   437  	})
   438  }
   439  
   440  func TestReadInConfig(t *testing.T) {
   441  	t.Run("config file set", func(t *testing.T) {
   442  		fs := afero.NewMemMapFs()
   443  
   444  		err := fs.Mkdir("/etc/viper", 0o777)
   445  		require.NoError(t, err)
   446  
   447  		file, err := fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
   448  		require.NoError(t, err)
   449  
   450  		_, err = file.Write([]byte(`key: value`))
   451  		require.NoError(t, err)
   452  
   453  		file.Close()
   454  
   455  		v := New()
   456  
   457  		v.SetFs(fs)
   458  		v.SetConfigFile(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
   459  
   460  		err = v.ReadInConfig()
   461  		require.NoError(t, err)
   462  
   463  		assert.Equal(t, "value", v.Get("key"))
   464  	})
   465  
   466  	t.Run("find file", func(t *testing.T) {
   467  		fs := afero.NewMemMapFs()
   468  
   469  		err := fs.Mkdir(testutil.AbsFilePath(t, "/etc/viper"), 0o777)
   470  		require.NoError(t, err)
   471  
   472  		file, err := fs.Create(testutil.AbsFilePath(t, "/etc/viper/config.yaml"))
   473  		require.NoError(t, err)
   474  
   475  		_, err = file.Write([]byte(`key: value`))
   476  		require.NoError(t, err)
   477  
   478  		file.Close()
   479  
   480  		v := New()
   481  
   482  		v.SetFs(fs)
   483  		v.AddConfigPath("/etc/viper")
   484  
   485  		err = v.ReadInConfig()
   486  		require.NoError(t, err)
   487  
   488  		assert.Equal(t, "value", v.Get("key"))
   489  	})
   490  }
   491  
   492  func TestDefault(t *testing.T) {
   493  	SetDefault("age", 45)
   494  	assert.Equal(t, 45, Get("age"))
   495  
   496  	SetDefault("clothing.jacket", "slacks")
   497  	assert.Equal(t, "slacks", Get("clothing.jacket"))
   498  
   499  	SetConfigType("yaml")
   500  	err := ReadConfig(bytes.NewBuffer(yamlExample))
   501  
   502  	assert.NoError(t, err)
   503  	assert.Equal(t, "leather", Get("clothing.jacket"))
   504  }
   505  
   506  func TestUnmarshaling(t *testing.T) {
   507  	SetConfigType("yaml")
   508  	r := bytes.NewReader(yamlExample)
   509  
   510  	unmarshalReader(r, v.config)
   511  	assert.True(t, InConfig("name"))
   512  	assert.True(t, InConfig("clothing.jacket"))
   513  	assert.False(t, InConfig("state"))
   514  	assert.False(t, InConfig("clothing.hat"))
   515  	assert.Equal(t, "steve", Get("name"))
   516  	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, Get("hobbies"))
   517  	assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, Get("clothing"))
   518  	assert.Equal(t, 35, Get("age"))
   519  }
   520  
   521  func TestUnmarshalExact(t *testing.T) {
   522  	vip := New()
   523  	target := &testUnmarshalExtra{}
   524  	vip.SetConfigType("yaml")
   525  	r := bytes.NewReader(yamlExampleWithExtras)
   526  	vip.ReadConfig(r)
   527  	err := vip.UnmarshalExact(target)
   528  	if err == nil {
   529  		t.Fatal("UnmarshalExact should error when populating a struct from a conf that contains unused fields")
   530  	}
   531  }
   532  
   533  func TestOverrides(t *testing.T) {
   534  	Set("age", 40)
   535  	assert.Equal(t, 40, Get("age"))
   536  }
   537  
   538  func TestDefaultPost(t *testing.T) {
   539  	assert.NotEqual(t, "NYC", Get("state"))
   540  	SetDefault("state", "NYC")
   541  	assert.Equal(t, "NYC", Get("state"))
   542  }
   543  
   544  func TestAliases(t *testing.T) {
   545  	RegisterAlias("years", "age")
   546  	assert.Equal(t, 40, Get("years"))
   547  	Set("years", 45)
   548  	assert.Equal(t, 45, Get("age"))
   549  }
   550  
   551  func TestAliasInConfigFile(t *testing.T) {
   552  	// the config file specifies "beard".  If we make this an alias for
   553  	// "hasbeard", we still want the old config file to work with beard.
   554  	RegisterAlias("beard", "hasbeard")
   555  	assert.Equal(t, true, Get("hasbeard"))
   556  	Set("hasbeard", false)
   557  	assert.Equal(t, false, Get("beard"))
   558  }
   559  
   560  func TestYML(t *testing.T) {
   561  	initYAML()
   562  	assert.Equal(t, "steve", Get("name"))
   563  }
   564  
   565  func TestJSON(t *testing.T) {
   566  	initJSON()
   567  	assert.Equal(t, "0001", Get("id"))
   568  }
   569  
   570  func TestProperties(t *testing.T) {
   571  	initProperties()
   572  	assert.Equal(t, "0001", Get("p_id"))
   573  }
   574  
   575  func TestTOML(t *testing.T) {
   576  	initTOML()
   577  	assert.Equal(t, "TOML Example", Get("title"))
   578  }
   579  
   580  func TestDotEnv(t *testing.T) {
   581  	initDotEnv()
   582  	assert.Equal(t, "DotEnv Example", Get("title_dotenv"))
   583  }
   584  
   585  func TestHCL(t *testing.T) {
   586  	initHcl()
   587  	assert.Equal(t, "0001", Get("id"))
   588  	assert.Equal(t, 0.55, Get("ppu"))
   589  	assert.Equal(t, "donut", Get("type"))
   590  	assert.Equal(t, "Cake", Get("name"))
   591  	Set("id", "0002")
   592  	assert.Equal(t, "0002", Get("id"))
   593  	assert.NotEqual(t, "cronut", Get("type"))
   594  }
   595  
   596  func TestIni(t *testing.T) {
   597  	initIni()
   598  	assert.Equal(t, "ini", Get("default.name"))
   599  }
   600  
   601  func TestRemotePrecedence(t *testing.T) {
   602  	initJSON()
   603  
   604  	remote := bytes.NewReader(remoteExample)
   605  	assert.Equal(t, "0001", Get("id"))
   606  	unmarshalReader(remote, v.kvstore)
   607  	assert.Equal(t, "0001", Get("id"))
   608  	assert.NotEqual(t, "cronut", Get("type"))
   609  	assert.Equal(t, "remote", Get("newkey"))
   610  	Set("newkey", "newvalue")
   611  	assert.NotEqual(t, "remote", Get("newkey"))
   612  	assert.Equal(t, "newvalue", Get("newkey"))
   613  	Set("newkey", "remote")
   614  }
   615  
   616  func TestEnv(t *testing.T) {
   617  	initJSON()
   618  
   619  	BindEnv("id")
   620  	BindEnv("f", "FOOD", "OLD_FOOD")
   621  
   622  	testutil.Setenv(t, "ID", "13")
   623  	testutil.Setenv(t, "FOOD", "apple")
   624  	testutil.Setenv(t, "OLD_FOOD", "banana")
   625  	testutil.Setenv(t, "NAME", "crunk")
   626  
   627  	assert.Equal(t, "13", Get("id"))
   628  	assert.Equal(t, "apple", Get("f"))
   629  	assert.Equal(t, "Cake", Get("name"))
   630  
   631  	AutomaticEnv()
   632  
   633  	assert.Equal(t, "crunk", Get("name"))
   634  }
   635  
   636  func TestMultipleEnv(t *testing.T) {
   637  	initJSON()
   638  
   639  	BindEnv("f", "FOOD", "OLD_FOOD")
   640  
   641  	testutil.Setenv(t, "OLD_FOOD", "banana")
   642  
   643  	assert.Equal(t, "banana", Get("f"))
   644  }
   645  
   646  func TestEmptyEnv(t *testing.T) {
   647  	initJSON()
   648  
   649  	BindEnv("type") // Empty environment variable
   650  	BindEnv("name") // Bound, but not set environment variable
   651  
   652  	testutil.Setenv(t, "TYPE", "")
   653  
   654  	assert.Equal(t, "donut", Get("type"))
   655  	assert.Equal(t, "Cake", Get("name"))
   656  }
   657  
   658  func TestEmptyEnv_Allowed(t *testing.T) {
   659  	initJSON()
   660  
   661  	AllowEmptyEnv(true)
   662  
   663  	BindEnv("type") // Empty environment variable
   664  	BindEnv("name") // Bound, but not set environment variable
   665  
   666  	testutil.Setenv(t, "TYPE", "")
   667  
   668  	assert.Equal(t, "", Get("type"))
   669  	assert.Equal(t, "Cake", Get("name"))
   670  }
   671  
   672  func TestEnvPrefix(t *testing.T) {
   673  	initJSON()
   674  
   675  	SetEnvPrefix("foo") // will be uppercased automatically
   676  	BindEnv("id")
   677  	BindEnv("f", "FOOD") // not using prefix
   678  
   679  	testutil.Setenv(t, "FOO_ID", "13")
   680  	testutil.Setenv(t, "FOOD", "apple")
   681  	testutil.Setenv(t, "FOO_NAME", "crunk")
   682  
   683  	assert.Equal(t, "13", Get("id"))
   684  	assert.Equal(t, "apple", Get("f"))
   685  	assert.Equal(t, "Cake", Get("name"))
   686  
   687  	AutomaticEnv()
   688  
   689  	assert.Equal(t, "crunk", Get("name"))
   690  }
   691  
   692  func TestAutoEnv(t *testing.T) {
   693  	Reset()
   694  
   695  	AutomaticEnv()
   696  
   697  	testutil.Setenv(t, "FOO_BAR", "13")
   698  
   699  	assert.Equal(t, "13", Get("foo_bar"))
   700  }
   701  
   702  func TestAutoEnvWithPrefix(t *testing.T) {
   703  	Reset()
   704  
   705  	AutomaticEnv()
   706  	SetEnvPrefix("Baz")
   707  
   708  	testutil.Setenv(t, "BAZ_BAR", "13")
   709  
   710  	assert.Equal(t, "13", Get("bar"))
   711  }
   712  
   713  func TestSetEnvKeyReplacer(t *testing.T) {
   714  	Reset()
   715  
   716  	AutomaticEnv()
   717  
   718  	testutil.Setenv(t, "REFRESH_INTERVAL", "30s")
   719  
   720  	replacer := strings.NewReplacer("-", "_")
   721  	SetEnvKeyReplacer(replacer)
   722  
   723  	assert.Equal(t, "30s", Get("refresh-interval"))
   724  }
   725  
   726  func TestEnvKeyReplacer(t *testing.T) {
   727  	v := NewWithOptions(EnvKeyReplacer(strings.NewReplacer("-", "_")))
   728  
   729  	v.AutomaticEnv()
   730  
   731  	testutil.Setenv(t, "REFRESH_INTERVAL", "30s")
   732  
   733  	assert.Equal(t, "30s", v.Get("refresh-interval"))
   734  }
   735  
   736  func TestEnvSubConfig(t *testing.T) {
   737  	initYAML()
   738  
   739  	v.AutomaticEnv()
   740  
   741  	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
   742  
   743  	testutil.Setenv(t, "CLOTHING_PANTS_SIZE", "small")
   744  	subv := v.Sub("clothing").Sub("pants")
   745  	assert.Equal(t, "small", subv.Get("size"))
   746  
   747  	// again with EnvPrefix
   748  	v.SetEnvPrefix("foo") // will be uppercased automatically
   749  	subWithPrefix := v.Sub("clothing").Sub("pants")
   750  	testutil.Setenv(t, "FOO_CLOTHING_PANTS_SIZE", "large")
   751  	assert.Equal(t, "large", subWithPrefix.Get("size"))
   752  }
   753  
   754  func TestAllKeys(t *testing.T) {
   755  	initConfigs()
   756  
   757  	ks := sort.StringSlice{
   758  		"title",
   759  		"author.bio",
   760  		"author.e-mail",
   761  		"author.github",
   762  		"author.name",
   763  		"newkey",
   764  		"owner.organization",
   765  		"owner.dob",
   766  		"owner.bio",
   767  		"name",
   768  		"beard",
   769  		"ppu",
   770  		"batters.batter",
   771  		"hobbies",
   772  		"clothing.jacket",
   773  		"clothing.trousers",
   774  		"default.import_path",
   775  		"default.name",
   776  		"default.version",
   777  		"clothing.pants.size",
   778  		"age",
   779  		"hacker",
   780  		"id",
   781  		"type",
   782  		"eyes",
   783  		"p_id",
   784  		"p_ppu",
   785  		"p_batters.batter.type",
   786  		"p_type",
   787  		"p_name",
   788  		"foos",
   789  		"title_dotenv",
   790  		"type_dotenv",
   791  		"name_dotenv",
   792  	}
   793  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
   794  	all := map[string]interface{}{
   795  		"owner": map[string]interface{}{
   796  			"organization": "MongoDB",
   797  			"bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
   798  			"dob":          dob,
   799  		},
   800  		"title": "TOML Example",
   801  		"author": map[string]interface{}{
   802  			"e-mail": "fake@localhost",
   803  			"github": "https://github.com/Unknown",
   804  			"name":   "Unknown",
   805  			"bio":    "Gopher.\nCoding addict.\nGood man.\n",
   806  		},
   807  		"ppu":  0.55,
   808  		"eyes": "brown",
   809  		"clothing": map[string]interface{}{
   810  			"trousers": "denim",
   811  			"jacket":   "leather",
   812  			"pants":    map[string]interface{}{"size": "large"},
   813  		},
   814  		"default": map[string]interface{}{
   815  			"import_path": "gopkg.in/ini.v1",
   816  			"name":        "ini",
   817  			"version":     "v1",
   818  		},
   819  		"id": "0001",
   820  		"batters": map[string]interface{}{
   821  			"batter": []interface{}{
   822  				map[string]interface{}{"type": "Regular"},
   823  				map[string]interface{}{"type": "Chocolate"},
   824  				map[string]interface{}{"type": "Blueberry"},
   825  				map[string]interface{}{"type": "Devil's Food"},
   826  			},
   827  		},
   828  		"hacker": true,
   829  		"beard":  true,
   830  		"hobbies": []interface{}{
   831  			"skateboarding",
   832  			"snowboarding",
   833  			"go",
   834  		},
   835  		"age":    35,
   836  		"type":   "donut",
   837  		"newkey": "remote",
   838  		"name":   "Cake",
   839  		"p_id":   "0001",
   840  		"p_ppu":  "0.55",
   841  		"p_name": "Cake",
   842  		"p_batters": map[string]interface{}{
   843  			"batter": map[string]interface{}{"type": "Regular"},
   844  		},
   845  		"p_type": "donut",
   846  		"foos": []map[string]interface{}{
   847  			{
   848  				"foo": []map[string]interface{}{
   849  					{"key": 1},
   850  					{"key": 2},
   851  					{"key": 3},
   852  					{"key": 4},
   853  				},
   854  			},
   855  		},
   856  		"title_dotenv": "DotEnv Example",
   857  		"type_dotenv":  "donut",
   858  		"name_dotenv":  "Cake",
   859  	}
   860  
   861  	allkeys := sort.StringSlice(AllKeys())
   862  	allkeys.Sort()
   863  	ks.Sort()
   864  
   865  	assert.Equal(t, ks, allkeys)
   866  	assert.Equal(t, all, AllSettings())
   867  }
   868  
   869  func TestAllKeysWithEnv(t *testing.T) {
   870  	v := New()
   871  
   872  	// bind and define environment variables (including a nested one)
   873  	v.BindEnv("id")
   874  	v.BindEnv("foo.bar")
   875  	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
   876  
   877  	testutil.Setenv(t, "ID", "13")
   878  	testutil.Setenv(t, "FOO_BAR", "baz")
   879  
   880  	expectedKeys := sort.StringSlice{"id", "foo.bar"}
   881  	expectedKeys.Sort()
   882  	keys := sort.StringSlice(v.AllKeys())
   883  	keys.Sort()
   884  	assert.Equal(t, expectedKeys, keys)
   885  }
   886  
   887  func TestAliasesOfAliases(t *testing.T) {
   888  	Set("Title", "Checking Case")
   889  	RegisterAlias("Foo", "Bar")
   890  	RegisterAlias("Bar", "Title")
   891  	assert.Equal(t, "Checking Case", Get("FOO"))
   892  }
   893  
   894  func TestRecursiveAliases(t *testing.T) {
   895  	Set("baz", "bat")
   896  	RegisterAlias("Baz", "Roo")
   897  	RegisterAlias("Roo", "baz")
   898  	assert.Equal(t, "bat", Get("Baz"))
   899  }
   900  
   901  func TestUnmarshal(t *testing.T) {
   902  	SetDefault("port", 1313)
   903  	Set("name", "Steve")
   904  	Set("duration", "1s1ms")
   905  	Set("modes", []int{1, 2, 3})
   906  
   907  	type config struct {
   908  		Port     int
   909  		Name     string
   910  		Duration time.Duration
   911  		Modes    []int
   912  	}
   913  
   914  	var C config
   915  
   916  	err := Unmarshal(&C)
   917  	if err != nil {
   918  		t.Fatalf("unable to decode into struct, %v", err)
   919  	}
   920  
   921  	assert.Equal(
   922  		t,
   923  		&config{
   924  			Name:     "Steve",
   925  			Port:     1313,
   926  			Duration: time.Second + time.Millisecond,
   927  			Modes:    []int{1, 2, 3},
   928  		},
   929  		&C,
   930  	)
   931  
   932  	Set("port", 1234)
   933  	err = Unmarshal(&C)
   934  	if err != nil {
   935  		t.Fatalf("unable to decode into struct, %v", err)
   936  	}
   937  
   938  	assert.Equal(
   939  		t,
   940  		&config{
   941  			Name:     "Steve",
   942  			Port:     1234,
   943  			Duration: time.Second + time.Millisecond,
   944  			Modes:    []int{1, 2, 3},
   945  		},
   946  		&C,
   947  	)
   948  }
   949  
   950  func TestUnmarshalWithDecoderOptions(t *testing.T) {
   951  	Set("credentials", "{\"foo\":\"bar\"}")
   952  
   953  	opt := DecodeHook(mapstructure.ComposeDecodeHookFunc(
   954  		mapstructure.StringToTimeDurationHookFunc(),
   955  		mapstructure.StringToSliceHookFunc(","),
   956  		// Custom Decode Hook Function
   957  		func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) {
   958  			if rf != reflect.String || rt != reflect.Map {
   959  				return data, nil
   960  			}
   961  			m := map[string]string{}
   962  			raw := data.(string)
   963  			if raw == "" {
   964  				return m, nil
   965  			}
   966  			return m, json.Unmarshal([]byte(raw), &m)
   967  		},
   968  	))
   969  
   970  	type config struct {
   971  		Credentials map[string]string
   972  	}
   973  
   974  	var C config
   975  
   976  	err := Unmarshal(&C, opt)
   977  	if err != nil {
   978  		t.Fatalf("unable to decode into struct, %v", err)
   979  	}
   980  
   981  	assert.Equal(t, &config{
   982  		Credentials: map[string]string{"foo": "bar"},
   983  	}, &C)
   984  }
   985  
   986  func TestBindPFlags(t *testing.T) {
   987  	v := New() // create independent Viper object
   988  	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
   989  
   990  	testValues := map[string]*string{
   991  		"host":     nil,
   992  		"port":     nil,
   993  		"endpoint": nil,
   994  	}
   995  
   996  	mutatedTestValues := map[string]string{
   997  		"host":     "localhost",
   998  		"port":     "6060",
   999  		"endpoint": "/public",
  1000  	}
  1001  
  1002  	for name := range testValues {
  1003  		testValues[name] = flagSet.String(name, "", "test")
  1004  	}
  1005  
  1006  	err := v.BindPFlags(flagSet)
  1007  	if err != nil {
  1008  		t.Fatalf("error binding flag set, %v", err)
  1009  	}
  1010  
  1011  	flagSet.VisitAll(func(flag *pflag.Flag) {
  1012  		flag.Value.Set(mutatedTestValues[flag.Name])
  1013  		flag.Changed = true
  1014  	})
  1015  
  1016  	for name, expected := range mutatedTestValues {
  1017  		assert.Equal(t, expected, v.Get(name))
  1018  	}
  1019  }
  1020  
  1021  //nolint:dupl
  1022  func TestBindPFlagsStringSlice(t *testing.T) {
  1023  	tests := []struct {
  1024  		Expected []string
  1025  		Value    string
  1026  	}{
  1027  		{[]string{}, ""},
  1028  		{[]string{"jeden"}, "jeden"},
  1029  		{[]string{"dwa", "trzy"}, "dwa,trzy"},
  1030  		{[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""},
  1031  	}
  1032  
  1033  	v := New() // create independent Viper object
  1034  	defaultVal := []string{"default"}
  1035  	v.SetDefault("stringslice", defaultVal)
  1036  
  1037  	for _, testValue := range tests {
  1038  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1039  		flagSet.StringSlice("stringslice", testValue.Expected, "test")
  1040  
  1041  		for _, changed := range []bool{true, false} {
  1042  			flagSet.VisitAll(func(f *pflag.Flag) {
  1043  				f.Value.Set(testValue.Value)
  1044  				f.Changed = changed
  1045  			})
  1046  
  1047  			err := v.BindPFlags(flagSet)
  1048  			if err != nil {
  1049  				t.Fatalf("error binding flag set, %v", err)
  1050  			}
  1051  
  1052  			type TestStr struct {
  1053  				StringSlice []string
  1054  			}
  1055  			val := &TestStr{}
  1056  			if err := v.Unmarshal(val); err != nil {
  1057  				t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
  1058  			}
  1059  			if changed {
  1060  				assert.Equal(t, testValue.Expected, val.StringSlice)
  1061  				assert.Equal(t, testValue.Expected, v.Get("stringslice"))
  1062  			} else {
  1063  				assert.Equal(t, defaultVal, val.StringSlice)
  1064  			}
  1065  		}
  1066  	}
  1067  }
  1068  
  1069  //nolint:dupl
  1070  func TestBindPFlagsStringArray(t *testing.T) {
  1071  	tests := []struct {
  1072  		Expected []string
  1073  		Value    string
  1074  	}{
  1075  		{[]string{}, ""},
  1076  		{[]string{"jeden"}, "jeden"},
  1077  		{[]string{"dwa,trzy"}, "dwa,trzy"},
  1078  		{[]string{"cztery,\"piec , szesc\""}, "cztery,\"piec , szesc\""},
  1079  	}
  1080  
  1081  	v := New() // create independent Viper object
  1082  	defaultVal := []string{"default"}
  1083  	v.SetDefault("stringarray", defaultVal)
  1084  
  1085  	for _, testValue := range tests {
  1086  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1087  		flagSet.StringArray("stringarray", testValue.Expected, "test")
  1088  
  1089  		for _, changed := range []bool{true, false} {
  1090  			flagSet.VisitAll(func(f *pflag.Flag) {
  1091  				f.Value.Set(testValue.Value)
  1092  				f.Changed = changed
  1093  			})
  1094  
  1095  			err := v.BindPFlags(flagSet)
  1096  			if err != nil {
  1097  				t.Fatalf("error binding flag set, %v", err)
  1098  			}
  1099  
  1100  			type TestStr struct {
  1101  				StringArray []string
  1102  			}
  1103  			val := &TestStr{}
  1104  			if err := v.Unmarshal(val); err != nil {
  1105  				t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
  1106  			}
  1107  			if changed {
  1108  				assert.Equal(t, testValue.Expected, val.StringArray)
  1109  				assert.Equal(t, testValue.Expected, v.Get("stringarray"))
  1110  			} else {
  1111  				assert.Equal(t, defaultVal, val.StringArray)
  1112  			}
  1113  		}
  1114  	}
  1115  }
  1116  
  1117  func TestSliceFlagsReturnCorrectType(t *testing.T) {
  1118  	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1119  	flagSet.IntSlice("int", []int{1, 2}, "")
  1120  	flagSet.StringSlice("str", []string{"3", "4"}, "")
  1121  	flagSet.DurationSlice("duration", []time.Duration{5 * time.Second}, "")
  1122  
  1123  	v := New()
  1124  	v.BindPFlags(flagSet)
  1125  
  1126  	all := v.AllSettings()
  1127  
  1128  	if _, ok := all["int"].([]int); !ok {
  1129  		t.Errorf("unexpected type %T expected []int", all["int"])
  1130  	}
  1131  	if _, ok := all["str"].([]string); !ok {
  1132  		t.Errorf("unexpected type %T expected []string", all["str"])
  1133  	}
  1134  	if _, ok := all["duration"].([]time.Duration); !ok {
  1135  		t.Errorf("unexpected type %T expected []time.Duration", all["duration"])
  1136  	}
  1137  }
  1138  
  1139  //nolint:dupl
  1140  func TestBindPFlagsIntSlice(t *testing.T) {
  1141  	tests := []struct {
  1142  		Expected []int
  1143  		Value    string
  1144  	}{
  1145  		{[]int{}, ""},
  1146  		{[]int{1}, "1"},
  1147  		{[]int{2, 3}, "2,3"},
  1148  	}
  1149  
  1150  	v := New() // create independent Viper object
  1151  	defaultVal := []int{0}
  1152  	v.SetDefault("intslice", defaultVal)
  1153  
  1154  	for _, testValue := range tests {
  1155  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1156  		flagSet.IntSlice("intslice", testValue.Expected, "test")
  1157  
  1158  		for _, changed := range []bool{true, false} {
  1159  			flagSet.VisitAll(func(f *pflag.Flag) {
  1160  				f.Value.Set(testValue.Value)
  1161  				f.Changed = changed
  1162  			})
  1163  
  1164  			err := v.BindPFlags(flagSet)
  1165  			if err != nil {
  1166  				t.Fatalf("error binding flag set, %v", err)
  1167  			}
  1168  
  1169  			type TestInt struct {
  1170  				IntSlice []int
  1171  			}
  1172  			val := &TestInt{}
  1173  			if err := v.Unmarshal(val); err != nil {
  1174  				t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
  1175  			}
  1176  			if changed {
  1177  				assert.Equal(t, testValue.Expected, val.IntSlice)
  1178  				assert.Equal(t, testValue.Expected, v.Get("intslice"))
  1179  			} else {
  1180  				assert.Equal(t, defaultVal, val.IntSlice)
  1181  			}
  1182  		}
  1183  	}
  1184  }
  1185  
  1186  func TestBindPFlag(t *testing.T) {
  1187  	testString := "testing"
  1188  	testValue := newStringValue(testString, &testString)
  1189  
  1190  	flag := &pflag.Flag{
  1191  		Name:    "testflag",
  1192  		Value:   testValue,
  1193  		Changed: false,
  1194  	}
  1195  
  1196  	BindPFlag("testvalue", flag)
  1197  
  1198  	assert.Equal(t, testString, Get("testvalue"))
  1199  
  1200  	flag.Value.Set("testing_mutate")
  1201  	flag.Changed = true // hack for pflag usage
  1202  
  1203  	assert.Equal(t, "testing_mutate", Get("testvalue"))
  1204  }
  1205  
  1206  func TestBindPFlagDetectNilFlag(t *testing.T) {
  1207  	result := BindPFlag("testvalue", nil)
  1208  	assert.Error(t, result)
  1209  }
  1210  
  1211  func TestBindPFlagStringToString(t *testing.T) {
  1212  	tests := []struct {
  1213  		Expected map[string]string
  1214  		Value    string
  1215  	}{
  1216  		{map[string]string{}, ""},
  1217  		{map[string]string{"yo": "hi"}, "yo=hi"},
  1218  		{map[string]string{"yo": "hi", "oh": "hi=there"}, "yo=hi,oh=hi=there"},
  1219  		{map[string]string{"yo": ""}, "yo="},
  1220  		{map[string]string{"yo": "", "oh": "hi=there"}, "yo=,oh=hi=there"},
  1221  	}
  1222  
  1223  	v := New() // create independent Viper object
  1224  	defaultVal := map[string]string{}
  1225  	v.SetDefault("stringtostring", defaultVal)
  1226  
  1227  	for _, testValue := range tests {
  1228  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1229  		flagSet.StringToString("stringtostring", testValue.Expected, "test")
  1230  
  1231  		for _, changed := range []bool{true, false} {
  1232  			flagSet.VisitAll(func(f *pflag.Flag) {
  1233  				f.Value.Set(testValue.Value)
  1234  				f.Changed = changed
  1235  			})
  1236  
  1237  			err := v.BindPFlags(flagSet)
  1238  			if err != nil {
  1239  				t.Fatalf("error binding flag set, %v", err)
  1240  			}
  1241  
  1242  			type TestMap struct {
  1243  				StringToString map[string]string
  1244  			}
  1245  			val := &TestMap{}
  1246  			if err := v.Unmarshal(val); err != nil {
  1247  				t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
  1248  			}
  1249  			if changed {
  1250  				assert.Equal(t, testValue.Expected, val.StringToString)
  1251  			} else {
  1252  				assert.Equal(t, defaultVal, val.StringToString)
  1253  			}
  1254  		}
  1255  	}
  1256  }
  1257  
  1258  func TestBoundCaseSensitivity(t *testing.T) {
  1259  	assert.Equal(t, "brown", Get("eyes"))
  1260  
  1261  	BindEnv("eYEs", "TURTLE_EYES")
  1262  
  1263  	testutil.Setenv(t, "TURTLE_EYES", "blue")
  1264  
  1265  	assert.Equal(t, "blue", Get("eyes"))
  1266  
  1267  	testString := "green"
  1268  	testValue := newStringValue(testString, &testString)
  1269  
  1270  	flag := &pflag.Flag{
  1271  		Name:    "eyeballs",
  1272  		Value:   testValue,
  1273  		Changed: true,
  1274  	}
  1275  
  1276  	BindPFlag("eYEs", flag)
  1277  	assert.Equal(t, "green", Get("eyes"))
  1278  }
  1279  
  1280  func TestSizeInBytes(t *testing.T) {
  1281  	input := map[string]uint{
  1282  		"":               0,
  1283  		"b":              0,
  1284  		"12 bytes":       0,
  1285  		"200000000000gb": 0,
  1286  		"12 b":           12,
  1287  		"43 MB":          43 * (1 << 20),
  1288  		"10mb":           10 * (1 << 20),
  1289  		"1gb":            1 << 30,
  1290  	}
  1291  
  1292  	for str, expected := range input {
  1293  		assert.Equal(t, expected, parseSizeInBytes(str), str)
  1294  	}
  1295  }
  1296  
  1297  func TestFindsNestedKeys(t *testing.T) {
  1298  	initConfigs()
  1299  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
  1300  
  1301  	Set("super", map[string]interface{}{
  1302  		"deep": map[string]interface{}{
  1303  			"nested": "value",
  1304  		},
  1305  	})
  1306  
  1307  	expected := map[string]interface{}{
  1308  		"super": map[string]interface{}{
  1309  			"deep": map[string]interface{}{
  1310  				"nested": "value",
  1311  			},
  1312  		},
  1313  		"super.deep": map[string]interface{}{
  1314  			"nested": "value",
  1315  		},
  1316  		"super.deep.nested":  "value",
  1317  		"owner.organization": "MongoDB",
  1318  		"batters.batter": []interface{}{
  1319  			map[string]interface{}{
  1320  				"type": "Regular",
  1321  			},
  1322  			map[string]interface{}{
  1323  				"type": "Chocolate",
  1324  			},
  1325  			map[string]interface{}{
  1326  				"type": "Blueberry",
  1327  			},
  1328  			map[string]interface{}{
  1329  				"type": "Devil's Food",
  1330  			},
  1331  		},
  1332  		"hobbies": []interface{}{
  1333  			"skateboarding", "snowboarding", "go",
  1334  		},
  1335  		"TITLE_DOTENV": "DotEnv Example",
  1336  		"TYPE_DOTENV":  "donut",
  1337  		"NAME_DOTENV":  "Cake",
  1338  		"title":        "TOML Example",
  1339  		"newkey":       "remote",
  1340  		"batters": map[string]interface{}{
  1341  			"batter": []interface{}{
  1342  				map[string]interface{}{
  1343  					"type": "Regular",
  1344  				},
  1345  				map[string]interface{}{
  1346  					"type": "Chocolate",
  1347  				},
  1348  				map[string]interface{}{
  1349  					"type": "Blueberry",
  1350  				},
  1351  				map[string]interface{}{
  1352  					"type": "Devil's Food",
  1353  				},
  1354  			},
  1355  		},
  1356  		"eyes": "brown",
  1357  		"age":  35,
  1358  		"owner": map[string]interface{}{
  1359  			"organization": "MongoDB",
  1360  			"bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
  1361  			"dob":          dob,
  1362  		},
  1363  		"owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large",
  1364  		"type":      "donut",
  1365  		"id":        "0001",
  1366  		"name":      "Cake",
  1367  		"hacker":    true,
  1368  		"ppu":       0.55,
  1369  		"clothing": map[string]interface{}{
  1370  			"jacket":   "leather",
  1371  			"trousers": "denim",
  1372  			"pants": map[string]interface{}{
  1373  				"size": "large",
  1374  			},
  1375  		},
  1376  		"clothing.jacket":     "leather",
  1377  		"clothing.pants.size": "large",
  1378  		"clothing.trousers":   "denim",
  1379  		"owner.dob":           dob,
  1380  		"beard":               true,
  1381  		"foos": []map[string]interface{}{
  1382  			{
  1383  				"foo": []map[string]interface{}{
  1384  					{
  1385  						"key": 1,
  1386  					},
  1387  					{
  1388  						"key": 2,
  1389  					},
  1390  					{
  1391  						"key": 3,
  1392  					},
  1393  					{
  1394  						"key": 4,
  1395  					},
  1396  				},
  1397  			},
  1398  		},
  1399  	}
  1400  
  1401  	for key, expectedValue := range expected {
  1402  		assert.Equal(t, expectedValue, v.Get(key))
  1403  	}
  1404  }
  1405  
  1406  func TestReadBufConfig(t *testing.T) {
  1407  	v := New()
  1408  	v.SetConfigType("yaml")
  1409  	v.ReadConfig(bytes.NewBuffer(yamlExample))
  1410  	t.Log(v.AllKeys())
  1411  
  1412  	assert.True(t, v.InConfig("name"))
  1413  	assert.True(t, v.InConfig("clothing.jacket"))
  1414  	assert.False(t, v.InConfig("state"))
  1415  	assert.False(t, v.InConfig("clothing.hat"))
  1416  	assert.Equal(t, "steve", v.Get("name"))
  1417  	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
  1418  	assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing"))
  1419  	assert.Equal(t, 35, v.Get("age"))
  1420  }
  1421  
  1422  func TestIsSet(t *testing.T) {
  1423  	v := New()
  1424  	v.SetConfigType("yaml")
  1425  
  1426  	/* config and defaults */
  1427  	v.ReadConfig(bytes.NewBuffer(yamlExample))
  1428  	v.SetDefault("clothing.shoes", "sneakers")
  1429  
  1430  	assert.True(t, v.IsSet("clothing"))
  1431  	assert.True(t, v.IsSet("clothing.jacket"))
  1432  	assert.False(t, v.IsSet("clothing.jackets"))
  1433  	assert.True(t, v.IsSet("clothing.shoes"))
  1434  
  1435  	/* state change */
  1436  	assert.False(t, v.IsSet("helloworld"))
  1437  	v.Set("helloworld", "fubar")
  1438  	assert.True(t, v.IsSet("helloworld"))
  1439  
  1440  	/* env */
  1441  	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
  1442  	v.BindEnv("eyes")
  1443  	v.BindEnv("foo")
  1444  	v.BindEnv("clothing.hat")
  1445  	v.BindEnv("clothing.hats")
  1446  
  1447  	testutil.Setenv(t, "FOO", "bar")
  1448  	testutil.Setenv(t, "CLOTHING_HAT", "bowler")
  1449  
  1450  	assert.True(t, v.IsSet("eyes"))           // in the config file
  1451  	assert.True(t, v.IsSet("foo"))            // in the environment
  1452  	assert.True(t, v.IsSet("clothing.hat"))   // in the environment
  1453  	assert.False(t, v.IsSet("clothing.hats")) // not defined
  1454  
  1455  	/* flags */
  1456  	flagset := pflag.NewFlagSet("testisset", pflag.ContinueOnError)
  1457  	flagset.Bool("foobaz", false, "foobaz")
  1458  	flagset.Bool("barbaz", false, "barbaz")
  1459  	foobaz, barbaz := flagset.Lookup("foobaz"), flagset.Lookup("barbaz")
  1460  	v.BindPFlag("foobaz", foobaz)
  1461  	v.BindPFlag("barbaz", barbaz)
  1462  	barbaz.Value.Set("true")
  1463  	barbaz.Changed = true // hack for pflag usage
  1464  
  1465  	assert.False(t, v.IsSet("foobaz"))
  1466  	assert.True(t, v.IsSet("barbaz"))
  1467  }
  1468  
  1469  func TestDirsSearch(t *testing.T) {
  1470  	root, config, cleanup := initDirs(t)
  1471  	defer cleanup()
  1472  
  1473  	v := New()
  1474  	v.SetConfigName(config)
  1475  	v.SetDefault(`key`, `default`)
  1476  
  1477  	entries, err := ioutil.ReadDir(root)
  1478  	assert.Nil(t, err)
  1479  	for _, e := range entries {
  1480  		if e.IsDir() {
  1481  			v.AddConfigPath(e.Name())
  1482  		}
  1483  	}
  1484  
  1485  	err = v.ReadInConfig()
  1486  	assert.Nil(t, err)
  1487  
  1488  	assert.Equal(t, `value is `+filepath.Base(v.configPaths[0]), v.GetString(`key`))
  1489  }
  1490  
  1491  func TestWrongDirsSearchNotFound(t *testing.T) {
  1492  	_, config, cleanup := initDirs(t)
  1493  	defer cleanup()
  1494  
  1495  	v := New()
  1496  	v.SetConfigName(config)
  1497  	v.SetDefault(`key`, `default`)
  1498  
  1499  	v.AddConfigPath(`whattayoutalkingbout`)
  1500  	v.AddConfigPath(`thispathaintthere`)
  1501  
  1502  	err := v.ReadInConfig()
  1503  	assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
  1504  
  1505  	// Even though config did not load and the error might have
  1506  	// been ignored by the client, the default still loads
  1507  	assert.Equal(t, `default`, v.GetString(`key`))
  1508  }
  1509  
  1510  func TestWrongDirsSearchNotFoundForMerge(t *testing.T) {
  1511  	_, config, cleanup := initDirs(t)
  1512  	defer cleanup()
  1513  
  1514  	v := New()
  1515  	v.SetConfigName(config)
  1516  	v.SetDefault(`key`, `default`)
  1517  
  1518  	v.AddConfigPath(`whattayoutalkingbout`)
  1519  	v.AddConfigPath(`thispathaintthere`)
  1520  
  1521  	err := v.MergeInConfig()
  1522  	assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
  1523  
  1524  	// Even though config did not load and the error might have
  1525  	// been ignored by the client, the default still loads
  1526  	assert.Equal(t, `default`, v.GetString(`key`))
  1527  }
  1528  
  1529  func TestSub(t *testing.T) {
  1530  	v := New()
  1531  	v.SetConfigType("yaml")
  1532  	v.ReadConfig(bytes.NewBuffer(yamlExample))
  1533  
  1534  	subv := v.Sub("clothing")
  1535  	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size"))
  1536  
  1537  	subv = v.Sub("clothing.pants")
  1538  	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size"))
  1539  
  1540  	subv = v.Sub("clothing.pants.size")
  1541  	assert.Equal(t, (*Viper)(nil), subv)
  1542  
  1543  	subv = v.Sub("missing.key")
  1544  	assert.Equal(t, (*Viper)(nil), subv)
  1545  
  1546  	subv = v.Sub("clothing")
  1547  	assert.Equal(t, subv.parents[0], "clothing")
  1548  
  1549  	subv = v.Sub("clothing").Sub("pants")
  1550  	assert.Equal(t, len(subv.parents), 2)
  1551  	assert.Equal(t, subv.parents[0], "clothing")
  1552  	assert.Equal(t, subv.parents[1], "pants")
  1553  }
  1554  
  1555  var hclWriteExpected = []byte(`"foos" = {
  1556    "foo" = {
  1557      "key" = 1
  1558    }
  1559  
  1560    "foo" = {
  1561      "key" = 2
  1562    }
  1563  
  1564    "foo" = {
  1565      "key" = 3
  1566    }
  1567  
  1568    "foo" = {
  1569      "key" = 4
  1570    }
  1571  }
  1572  
  1573  "id" = "0001"
  1574  
  1575  "name" = "Cake"
  1576  
  1577  "ppu" = 0.55
  1578  
  1579  "type" = "donut"`)
  1580  
  1581  var jsonWriteExpected = []byte(`{
  1582    "batters": {
  1583      "batter": [
  1584        {
  1585          "type": "Regular"
  1586        },
  1587        {
  1588          "type": "Chocolate"
  1589        },
  1590        {
  1591          "type": "Blueberry"
  1592        },
  1593        {
  1594          "type": "Devil's Food"
  1595        }
  1596      ]
  1597    },
  1598    "id": "0001",
  1599    "name": "Cake",
  1600    "ppu": 0.55,
  1601    "type": "donut"
  1602  }`)
  1603  
  1604  var propertiesWriteExpected = []byte(`p_id = 0001
  1605  p_type = donut
  1606  p_name = Cake
  1607  p_ppu = 0.55
  1608  p_batters.batter.type = Regular
  1609  `)
  1610  
  1611  // var yamlWriteExpected = []byte(`age: 35
  1612  // beard: true
  1613  // clothing:
  1614  //     jacket: leather
  1615  //     pants:
  1616  //         size: large
  1617  //     trousers: denim
  1618  // eyes: brown
  1619  // hacker: true
  1620  // hobbies:
  1621  //     - skateboarding
  1622  //     - snowboarding
  1623  //     - go
  1624  // name: steve
  1625  // `)
  1626  
  1627  func TestWriteConfig(t *testing.T) {
  1628  	fs := afero.NewMemMapFs()
  1629  	testCases := map[string]struct {
  1630  		configName      string
  1631  		inConfigType    string
  1632  		outConfigType   string
  1633  		fileName        string
  1634  		input           []byte
  1635  		expectedContent []byte
  1636  	}{
  1637  		"hcl with file extension": {
  1638  			configName:      "c",
  1639  			inConfigType:    "hcl",
  1640  			outConfigType:   "hcl",
  1641  			fileName:        "c.hcl",
  1642  			input:           hclExample,
  1643  			expectedContent: hclWriteExpected,
  1644  		},
  1645  		"hcl without file extension": {
  1646  			configName:      "c",
  1647  			inConfigType:    "hcl",
  1648  			outConfigType:   "hcl",
  1649  			fileName:        "c",
  1650  			input:           hclExample,
  1651  			expectedContent: hclWriteExpected,
  1652  		},
  1653  		"hcl with file extension and mismatch type": {
  1654  			configName:      "c",
  1655  			inConfigType:    "hcl",
  1656  			outConfigType:   "json",
  1657  			fileName:        "c.hcl",
  1658  			input:           hclExample,
  1659  			expectedContent: hclWriteExpected,
  1660  		},
  1661  		"json with file extension": {
  1662  			configName:      "c",
  1663  			inConfigType:    "json",
  1664  			outConfigType:   "json",
  1665  			fileName:        "c.json",
  1666  			input:           jsonExample,
  1667  			expectedContent: jsonWriteExpected,
  1668  		},
  1669  		"json without file extension": {
  1670  			configName:      "c",
  1671  			inConfigType:    "json",
  1672  			outConfigType:   "json",
  1673  			fileName:        "c",
  1674  			input:           jsonExample,
  1675  			expectedContent: jsonWriteExpected,
  1676  		},
  1677  		"json with file extension and mismatch type": {
  1678  			configName:      "c",
  1679  			inConfigType:    "json",
  1680  			outConfigType:   "hcl",
  1681  			fileName:        "c.json",
  1682  			input:           jsonExample,
  1683  			expectedContent: jsonWriteExpected,
  1684  		},
  1685  		"properties with file extension": {
  1686  			configName:      "c",
  1687  			inConfigType:    "properties",
  1688  			outConfigType:   "properties",
  1689  			fileName:        "c.properties",
  1690  			input:           propertiesExample,
  1691  			expectedContent: propertiesWriteExpected,
  1692  		},
  1693  		"properties without file extension": {
  1694  			configName:      "c",
  1695  			inConfigType:    "properties",
  1696  			outConfigType:   "properties",
  1697  			fileName:        "c",
  1698  			input:           propertiesExample,
  1699  			expectedContent: propertiesWriteExpected,
  1700  		},
  1701  		"yaml with file extension": {
  1702  			configName:      "c",
  1703  			inConfigType:    "yaml",
  1704  			outConfigType:   "yaml",
  1705  			fileName:        "c.yaml",
  1706  			input:           yamlExample,
  1707  			expectedContent: yamlWriteExpected,
  1708  		},
  1709  		"yaml without file extension": {
  1710  			configName:      "c",
  1711  			inConfigType:    "yaml",
  1712  			outConfigType:   "yaml",
  1713  			fileName:        "c",
  1714  			input:           yamlExample,
  1715  			expectedContent: yamlWriteExpected,
  1716  		},
  1717  		"yaml with file extension and mismatch type": {
  1718  			configName:      "c",
  1719  			inConfigType:    "yaml",
  1720  			outConfigType:   "json",
  1721  			fileName:        "c.yaml",
  1722  			input:           yamlExample,
  1723  			expectedContent: yamlWriteExpected,
  1724  		},
  1725  	}
  1726  	for name, tc := range testCases {
  1727  		t.Run(name, func(t *testing.T) {
  1728  			v := New()
  1729  			v.SetFs(fs)
  1730  			v.SetConfigName(tc.fileName)
  1731  			v.SetConfigType(tc.inConfigType)
  1732  
  1733  			err := v.ReadConfig(bytes.NewBuffer(tc.input))
  1734  			if err != nil {
  1735  				t.Fatal(err)
  1736  			}
  1737  			v.SetConfigType(tc.outConfigType)
  1738  			if err := v.WriteConfigAs(tc.fileName); err != nil {
  1739  				t.Fatal(err)
  1740  			}
  1741  			read, err := afero.ReadFile(fs, tc.fileName)
  1742  			if err != nil {
  1743  				t.Fatal(err)
  1744  			}
  1745  			assert.Equal(t, tc.expectedContent, read)
  1746  		})
  1747  	}
  1748  }
  1749  
  1750  func TestWriteConfigTOML(t *testing.T) {
  1751  	fs := afero.NewMemMapFs()
  1752  
  1753  	testCases := map[string]struct {
  1754  		configName string
  1755  		configType string
  1756  		fileName   string
  1757  		input      []byte
  1758  	}{
  1759  		"with file extension": {
  1760  			configName: "c",
  1761  			configType: "toml",
  1762  			fileName:   "c.toml",
  1763  			input:      tomlExample,
  1764  		},
  1765  		"without file extension": {
  1766  			configName: "c",
  1767  			configType: "toml",
  1768  			fileName:   "c",
  1769  			input:      tomlExample,
  1770  		},
  1771  	}
  1772  	for name, tc := range testCases {
  1773  		t.Run(name, func(t *testing.T) {
  1774  			v := New()
  1775  			v.SetFs(fs)
  1776  			v.SetConfigName(tc.configName)
  1777  			v.SetConfigType(tc.configType)
  1778  			err := v.ReadConfig(bytes.NewBuffer(tc.input))
  1779  			if err != nil {
  1780  				t.Fatal(err)
  1781  			}
  1782  			if err := v.WriteConfigAs(tc.fileName); err != nil {
  1783  				t.Fatal(err)
  1784  			}
  1785  
  1786  			// The TOML String method does not order the contents.
  1787  			// Therefore, we must read the generated file and compare the data.
  1788  			v2 := New()
  1789  			v2.SetFs(fs)
  1790  			v2.SetConfigName(tc.configName)
  1791  			v2.SetConfigType(tc.configType)
  1792  			v2.SetConfigFile(tc.fileName)
  1793  			err = v2.ReadInConfig()
  1794  			if err != nil {
  1795  				t.Fatal(err)
  1796  			}
  1797  
  1798  			assert.Equal(t, v.GetString("title"), v2.GetString("title"))
  1799  			assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio"))
  1800  			assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob"))
  1801  			assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization"))
  1802  		})
  1803  	}
  1804  }
  1805  
  1806  func TestWriteConfigDotEnv(t *testing.T) {
  1807  	fs := afero.NewMemMapFs()
  1808  	testCases := map[string]struct {
  1809  		configName string
  1810  		configType string
  1811  		fileName   string
  1812  		input      []byte
  1813  	}{
  1814  		"with file extension": {
  1815  			configName: "c",
  1816  			configType: "env",
  1817  			fileName:   "c.env",
  1818  			input:      dotenvExample,
  1819  		},
  1820  		"without file extension": {
  1821  			configName: "c",
  1822  			configType: "env",
  1823  			fileName:   "c",
  1824  			input:      dotenvExample,
  1825  		},
  1826  	}
  1827  	for name, tc := range testCases {
  1828  		t.Run(name, func(t *testing.T) {
  1829  			v := New()
  1830  			v.SetFs(fs)
  1831  			v.SetConfigName(tc.configName)
  1832  			v.SetConfigType(tc.configType)
  1833  			err := v.ReadConfig(bytes.NewBuffer(tc.input))
  1834  			if err != nil {
  1835  				t.Fatal(err)
  1836  			}
  1837  			if err := v.WriteConfigAs(tc.fileName); err != nil {
  1838  				t.Fatal(err)
  1839  			}
  1840  
  1841  			// The TOML String method does not order the contents.
  1842  			// Therefore, we must read the generated file and compare the data.
  1843  			v2 := New()
  1844  			v2.SetFs(fs)
  1845  			v2.SetConfigName(tc.configName)
  1846  			v2.SetConfigType(tc.configType)
  1847  			v2.SetConfigFile(tc.fileName)
  1848  			err = v2.ReadInConfig()
  1849  			if err != nil {
  1850  				t.Fatal(err)
  1851  			}
  1852  
  1853  			assert.Equal(t, v.GetString("title_dotenv"), v2.GetString("title_dotenv"))
  1854  			assert.Equal(t, v.GetString("type_dotenv"), v2.GetString("type_dotenv"))
  1855  			assert.Equal(t, v.GetString("kind_dotenv"), v2.GetString("kind_dotenv"))
  1856  		})
  1857  	}
  1858  }
  1859  
  1860  func TestSafeWriteConfig(t *testing.T) {
  1861  	v := New()
  1862  	fs := afero.NewMemMapFs()
  1863  	v.SetFs(fs)
  1864  	v.AddConfigPath("/test")
  1865  	v.SetConfigName("c")
  1866  	v.SetConfigType("yaml")
  1867  	require.NoError(t, v.ReadConfig(bytes.NewBuffer(yamlExample)))
  1868  	require.NoError(t, v.SafeWriteConfig())
  1869  	read, err := afero.ReadFile(fs, testutil.AbsFilePath(t, "/test/c.yaml"))
  1870  	require.NoError(t, err)
  1871  	assert.Equal(t, yamlWriteExpected, read)
  1872  }
  1873  
  1874  func TestSafeWriteConfigWithMissingConfigPath(t *testing.T) {
  1875  	v := New()
  1876  	fs := afero.NewMemMapFs()
  1877  	v.SetFs(fs)
  1878  	v.SetConfigName("c")
  1879  	v.SetConfigType("yaml")
  1880  	require.EqualError(t, v.SafeWriteConfig(), "missing configuration for 'configPath'")
  1881  }
  1882  
  1883  func TestSafeWriteConfigWithExistingFile(t *testing.T) {
  1884  	v := New()
  1885  	fs := afero.NewMemMapFs()
  1886  	fs.Create(testutil.AbsFilePath(t, "/test/c.yaml"))
  1887  	v.SetFs(fs)
  1888  	v.AddConfigPath("/test")
  1889  	v.SetConfigName("c")
  1890  	v.SetConfigType("yaml")
  1891  	err := v.SafeWriteConfig()
  1892  	require.Error(t, err)
  1893  	_, ok := err.(ConfigFileAlreadyExistsError)
  1894  	assert.True(t, ok, "Expected ConfigFileAlreadyExistsError")
  1895  }
  1896  
  1897  func TestSafeWriteAsConfig(t *testing.T) {
  1898  	v := New()
  1899  	fs := afero.NewMemMapFs()
  1900  	v.SetFs(fs)
  1901  	err := v.ReadConfig(bytes.NewBuffer(yamlExample))
  1902  	if err != nil {
  1903  		t.Fatal(err)
  1904  	}
  1905  	require.NoError(t, v.SafeWriteConfigAs("/test/c.yaml"))
  1906  	if _, err = afero.ReadFile(fs, "/test/c.yaml"); err != nil {
  1907  		t.Fatal(err)
  1908  	}
  1909  }
  1910  
  1911  func TestSafeWriteConfigAsWithExistingFile(t *testing.T) {
  1912  	v := New()
  1913  	fs := afero.NewMemMapFs()
  1914  	fs.Create("/test/c.yaml")
  1915  	v.SetFs(fs)
  1916  	err := v.SafeWriteConfigAs("/test/c.yaml")
  1917  	require.Error(t, err)
  1918  	_, ok := err.(ConfigFileAlreadyExistsError)
  1919  	assert.True(t, ok, "Expected ConfigFileAlreadyExistsError")
  1920  }
  1921  
  1922  func TestWriteHiddenFile(t *testing.T) {
  1923  	v := New()
  1924  	fs := afero.NewMemMapFs()
  1925  	fs.Create(testutil.AbsFilePath(t, "/test/.config"))
  1926  	v.SetFs(fs)
  1927  
  1928  	v.SetConfigName(".config")
  1929  	v.SetConfigType("yaml")
  1930  	v.AddConfigPath("/test")
  1931  
  1932  	err := v.ReadInConfig()
  1933  	require.NoError(t, err)
  1934  
  1935  	err = v.WriteConfig()
  1936  	require.NoError(t, err)
  1937  }
  1938  
  1939  var yamlMergeExampleTgt = []byte(`
  1940  hello:
  1941      pop: 37890
  1942      largenum: 765432101234567
  1943      num2pow63: 9223372036854775808
  1944      universe: null
  1945      world:
  1946      - us
  1947      - uk
  1948      - fr
  1949      - de
  1950  `)
  1951  
  1952  var yamlMergeExampleSrc = []byte(`
  1953  hello:
  1954      pop: 45000
  1955      largenum: 7654321001234567
  1956      universe:
  1957      - mw
  1958      - ad
  1959      ints:
  1960      - 1
  1961      - 2
  1962  fu: bar
  1963  `)
  1964  
  1965  var jsonMergeExampleTgt = []byte(`
  1966  {
  1967  	"hello": {
  1968  		"foo": null,
  1969  		"pop": 123456
  1970  	}
  1971  }
  1972  `)
  1973  
  1974  var jsonMergeExampleSrc = []byte(`
  1975  {
  1976  	"hello": {
  1977  		"foo": "foo str",
  1978  		"pop": "pop str"
  1979  	}
  1980  }
  1981  `)
  1982  
  1983  func TestMergeConfig(t *testing.T) {
  1984  	v := New()
  1985  	v.SetConfigType("yml")
  1986  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  1987  		t.Fatal(err)
  1988  	}
  1989  
  1990  	if pop := v.GetInt("hello.pop"); pop != 37890 {
  1991  		t.Fatalf("pop != 37890, = %d", pop)
  1992  	}
  1993  
  1994  	if pop := v.GetInt32("hello.pop"); pop != int32(37890) {
  1995  		t.Fatalf("pop != 37890, = %d", pop)
  1996  	}
  1997  
  1998  	if pop := v.GetInt64("hello.largenum"); pop != int64(765432101234567) {
  1999  		t.Fatalf("int64 largenum != 765432101234567, = %d", pop)
  2000  	}
  2001  
  2002  	if pop := v.GetUint("hello.pop"); pop != 37890 {
  2003  		t.Fatalf("uint pop != 37890, = %d", pop)
  2004  	}
  2005  
  2006  	if pop := v.GetUint16("hello.pop"); pop != uint16(37890) {
  2007  		t.Fatalf("uint pop != 37890, = %d", pop)
  2008  	}
  2009  
  2010  	if pop := v.GetUint32("hello.pop"); pop != 37890 {
  2011  		t.Fatalf("uint32 pop != 37890, = %d", pop)
  2012  	}
  2013  
  2014  	if pop := v.GetUint64("hello.num2pow63"); pop != 9223372036854775808 {
  2015  		t.Fatalf("uint64 num2pow63 != 9223372036854775808, = %d", pop)
  2016  	}
  2017  
  2018  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  2019  		t.Fatalf("len(world) != 4, = %d", len(world))
  2020  	}
  2021  
  2022  	if fu := v.GetString("fu"); fu != "" {
  2023  		t.Fatalf("fu != \"\", = %s", fu)
  2024  	}
  2025  
  2026  	if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
  2027  		t.Fatal(err)
  2028  	}
  2029  
  2030  	if pop := v.GetInt("hello.pop"); pop != 45000 {
  2031  		t.Fatalf("pop != 45000, = %d", pop)
  2032  	}
  2033  
  2034  	if pop := v.GetInt32("hello.pop"); pop != int32(45000) {
  2035  		t.Fatalf("pop != 45000, = %d", pop)
  2036  	}
  2037  
  2038  	if pop := v.GetInt64("hello.largenum"); pop != int64(7654321001234567) {
  2039  		t.Fatalf("int64 largenum != 7654321001234567, = %d", pop)
  2040  	}
  2041  
  2042  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  2043  		t.Fatalf("len(world) != 4, = %d", len(world))
  2044  	}
  2045  
  2046  	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
  2047  		t.Fatalf("len(universe) != 2, = %d", len(universe))
  2048  	}
  2049  
  2050  	if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 {
  2051  		t.Fatalf("len(ints) != 2, = %d", len(ints))
  2052  	}
  2053  
  2054  	if fu := v.GetString("fu"); fu != "bar" {
  2055  		t.Fatalf("fu != \"bar\", = %s", fu)
  2056  	}
  2057  }
  2058  
  2059  func TestMergeConfigOverrideType(t *testing.T) {
  2060  	v := New()
  2061  	v.SetConfigType("json")
  2062  	if err := v.ReadConfig(bytes.NewBuffer(jsonMergeExampleTgt)); err != nil {
  2063  		t.Fatal(err)
  2064  	}
  2065  
  2066  	if err := v.MergeConfig(bytes.NewBuffer(jsonMergeExampleSrc)); err != nil {
  2067  		t.Fatal(err)
  2068  	}
  2069  
  2070  	if pop := v.GetString("hello.pop"); pop != "pop str" {
  2071  		t.Fatalf("pop != \"pop str\", = %s", pop)
  2072  	}
  2073  
  2074  	if foo := v.GetString("hello.foo"); foo != "foo str" {
  2075  		t.Fatalf("foo != \"foo str\", = %s", foo)
  2076  	}
  2077  }
  2078  
  2079  func TestMergeConfigNoMerge(t *testing.T) {
  2080  	v := New()
  2081  	v.SetConfigType("yml")
  2082  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  2083  		t.Fatal(err)
  2084  	}
  2085  
  2086  	if pop := v.GetInt("hello.pop"); pop != 37890 {
  2087  		t.Fatalf("pop != 37890, = %d", pop)
  2088  	}
  2089  
  2090  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  2091  		t.Fatalf("len(world) != 4, = %d", len(world))
  2092  	}
  2093  
  2094  	if fu := v.GetString("fu"); fu != "" {
  2095  		t.Fatalf("fu != \"\", = %s", fu)
  2096  	}
  2097  
  2098  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
  2099  		t.Fatal(err)
  2100  	}
  2101  
  2102  	if pop := v.GetInt("hello.pop"); pop != 45000 {
  2103  		t.Fatalf("pop != 45000, = %d", pop)
  2104  	}
  2105  
  2106  	if world := v.GetStringSlice("hello.world"); len(world) != 0 {
  2107  		t.Fatalf("len(world) != 0, = %d", len(world))
  2108  	}
  2109  
  2110  	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
  2111  		t.Fatalf("len(universe) != 2, = %d", len(universe))
  2112  	}
  2113  
  2114  	if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 {
  2115  		t.Fatalf("len(ints) != 2, = %d", len(ints))
  2116  	}
  2117  
  2118  	if fu := v.GetString("fu"); fu != "bar" {
  2119  		t.Fatalf("fu != \"bar\", = %s", fu)
  2120  	}
  2121  }
  2122  
  2123  func TestMergeConfigMap(t *testing.T) {
  2124  	v := New()
  2125  	v.SetConfigType("yml")
  2126  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  2127  		t.Fatal(err)
  2128  	}
  2129  
  2130  	assert := func(i int) {
  2131  		large := v.GetInt64("hello.largenum")
  2132  		pop := v.GetInt("hello.pop")
  2133  		if large != 765432101234567 {
  2134  			t.Fatal("Got large num:", large)
  2135  		}
  2136  
  2137  		if pop != i {
  2138  			t.Fatal("Got pop:", pop)
  2139  		}
  2140  	}
  2141  
  2142  	assert(37890)
  2143  
  2144  	update := map[string]interface{}{
  2145  		"Hello": map[string]interface{}{
  2146  			"Pop": 1234,
  2147  		},
  2148  		"World": map[interface{}]interface{}{
  2149  			"Rock": 345,
  2150  		},
  2151  	}
  2152  
  2153  	if err := v.MergeConfigMap(update); err != nil {
  2154  		t.Fatal(err)
  2155  	}
  2156  
  2157  	if rock := v.GetInt("world.rock"); rock != 345 {
  2158  		t.Fatal("Got rock:", rock)
  2159  	}
  2160  
  2161  	assert(1234)
  2162  }
  2163  
  2164  func TestUnmarshalingWithAliases(t *testing.T) {
  2165  	v := New()
  2166  	v.SetDefault("ID", 1)
  2167  	v.Set("name", "Steve")
  2168  	v.Set("lastname", "Owen")
  2169  
  2170  	v.RegisterAlias("UserID", "ID")
  2171  	v.RegisterAlias("Firstname", "name")
  2172  	v.RegisterAlias("Surname", "lastname")
  2173  
  2174  	type config struct {
  2175  		ID        int
  2176  		FirstName string
  2177  		Surname   string
  2178  	}
  2179  
  2180  	var C config
  2181  	err := v.Unmarshal(&C)
  2182  	if err != nil {
  2183  		t.Fatalf("unable to decode into struct, %v", err)
  2184  	}
  2185  
  2186  	assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C)
  2187  }
  2188  
  2189  func TestSetConfigNameClearsFileCache(t *testing.T) {
  2190  	SetConfigFile("/tmp/config.yaml")
  2191  	SetConfigName("default")
  2192  	f, err := v.getConfigFile()
  2193  	if err == nil {
  2194  		t.Fatalf("config file cache should have been cleared")
  2195  	}
  2196  	assert.Empty(t, f)
  2197  }
  2198  
  2199  func TestShadowedNestedValue(t *testing.T) {
  2200  	config := `name: steve
  2201  clothing:
  2202    jacket: leather
  2203    trousers: denim
  2204    pants:
  2205      size: large
  2206  `
  2207  	initConfig("yaml", config)
  2208  
  2209  	assert.Equal(t, "steve", GetString("name"))
  2210  
  2211  	polyester := "polyester"
  2212  	SetDefault("clothing.shirt", polyester)
  2213  	SetDefault("clothing.jacket.price", 100)
  2214  
  2215  	assert.Equal(t, "leather", GetString("clothing.jacket"))
  2216  	assert.Nil(t, Get("clothing.jacket.price"))
  2217  	assert.Equal(t, polyester, GetString("clothing.shirt"))
  2218  
  2219  	clothingSettings := AllSettings()["clothing"].(map[string]interface{})
  2220  	assert.Equal(t, "leather", clothingSettings["jacket"])
  2221  	assert.Equal(t, polyester, clothingSettings["shirt"])
  2222  }
  2223  
  2224  func TestDotParameter(t *testing.T) {
  2225  	initJSON()
  2226  	// shoud take precedence over batters defined in jsonExample
  2227  	r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`))
  2228  	unmarshalReader(r, v.config)
  2229  
  2230  	actual := Get("batters.batter")
  2231  	expected := []interface{}{map[string]interface{}{"type": "Small"}}
  2232  	assert.Equal(t, expected, actual)
  2233  }
  2234  
  2235  func TestCaseInsensitive(t *testing.T) {
  2236  	for _, config := range []struct {
  2237  		typ     string
  2238  		content string
  2239  	}{
  2240  		{"yaml", `
  2241  aBcD: 1
  2242  eF:
  2243    gH: 2
  2244    iJk: 3
  2245    Lm:
  2246      nO: 4
  2247      P:
  2248        Q: 5
  2249        R: 6
  2250  `},
  2251  		{"json", `{
  2252    "aBcD": 1,
  2253    "eF": {
  2254      "iJk": 3,
  2255      "Lm": {
  2256        "P": {
  2257          "Q": 5,
  2258          "R": 6
  2259        },
  2260        "nO": 4
  2261      },
  2262      "gH": 2
  2263    }
  2264  }`},
  2265  		{"toml", `aBcD = 1
  2266  [eF]
  2267  gH = 2
  2268  iJk = 3
  2269  [eF.Lm]
  2270  nO = 4
  2271  [eF.Lm.P]
  2272  Q = 5
  2273  R = 6
  2274  `},
  2275  	} {
  2276  		doTestCaseInsensitive(t, config.typ, config.content)
  2277  	}
  2278  }
  2279  
  2280  func TestCaseInsensitiveSet(t *testing.T) {
  2281  	Reset()
  2282  	m1 := map[string]interface{}{
  2283  		"Foo": 32,
  2284  		"Bar": map[interface{}]interface{}{
  2285  			"ABc": "A",
  2286  			"cDE": "B",
  2287  		},
  2288  	}
  2289  
  2290  	m2 := map[string]interface{}{
  2291  		"Foo": 52,
  2292  		"Bar": map[interface{}]interface{}{
  2293  			"bCd": "A",
  2294  			"eFG": "B",
  2295  		},
  2296  	}
  2297  
  2298  	Set("Given1", m1)
  2299  	Set("Number1", 42)
  2300  
  2301  	SetDefault("Given2", m2)
  2302  	SetDefault("Number2", 52)
  2303  
  2304  	// Verify SetDefault
  2305  	if v := Get("number2"); v != 52 {
  2306  		t.Fatalf("Expected 52 got %q", v)
  2307  	}
  2308  
  2309  	if v := Get("given2.foo"); v != 52 {
  2310  		t.Fatalf("Expected 52 got %q", v)
  2311  	}
  2312  
  2313  	if v := Get("given2.bar.bcd"); v != "A" {
  2314  		t.Fatalf("Expected A got %q", v)
  2315  	}
  2316  
  2317  	if _, ok := m2["Foo"]; !ok {
  2318  		t.Fatal("Input map changed")
  2319  	}
  2320  
  2321  	// Verify Set
  2322  	if v := Get("number1"); v != 42 {
  2323  		t.Fatalf("Expected 42 got %q", v)
  2324  	}
  2325  
  2326  	if v := Get("given1.foo"); v != 32 {
  2327  		t.Fatalf("Expected 32 got %q", v)
  2328  	}
  2329  
  2330  	if v := Get("given1.bar.abc"); v != "A" {
  2331  		t.Fatalf("Expected A got %q", v)
  2332  	}
  2333  
  2334  	if _, ok := m1["Foo"]; !ok {
  2335  		t.Fatal("Input map changed")
  2336  	}
  2337  }
  2338  
  2339  func TestParseNested(t *testing.T) {
  2340  	type duration struct {
  2341  		Delay time.Duration
  2342  	}
  2343  
  2344  	type item struct {
  2345  		Name   string
  2346  		Delay  time.Duration
  2347  		Nested duration
  2348  	}
  2349  
  2350  	config := `[[parent]]
  2351  	delay="100ms"
  2352  	[parent.nested]
  2353  	delay="200ms"
  2354  `
  2355  	initConfig("toml", config)
  2356  
  2357  	var items []item
  2358  	err := v.UnmarshalKey("parent", &items)
  2359  	if err != nil {
  2360  		t.Fatalf("unable to decode into struct, %v", err)
  2361  	}
  2362  
  2363  	assert.Equal(t, 1, len(items))
  2364  	assert.Equal(t, 100*time.Millisecond, items[0].Delay)
  2365  	assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay)
  2366  }
  2367  
  2368  func doTestCaseInsensitive(t *testing.T, typ, config string) {
  2369  	initConfig(typ, config)
  2370  	Set("RfD", true)
  2371  	assert.Equal(t, true, Get("rfd"))
  2372  	assert.Equal(t, true, Get("rFD"))
  2373  	assert.Equal(t, 1, cast.ToInt(Get("abcd")))
  2374  	assert.Equal(t, 1, cast.ToInt(Get("Abcd")))
  2375  	assert.Equal(t, 2, cast.ToInt(Get("ef.gh")))
  2376  	assert.Equal(t, 3, cast.ToInt(Get("ef.ijk")))
  2377  	assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no")))
  2378  	assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q")))
  2379  }
  2380  
  2381  func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) {
  2382  	watchDir, err := ioutil.TempDir("", "")
  2383  	require.Nil(t, err)
  2384  	configFile := path.Join(watchDir, "config.yaml")
  2385  	err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0o640)
  2386  	require.Nil(t, err)
  2387  	cleanup := func() {
  2388  		os.RemoveAll(watchDir)
  2389  	}
  2390  	v := New()
  2391  	v.SetConfigFile(configFile)
  2392  	err = v.ReadInConfig()
  2393  	require.Nil(t, err)
  2394  	require.Equal(t, "bar", v.Get("foo"))
  2395  	return v, configFile, cleanup
  2396  }
  2397  
  2398  func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func()) {
  2399  	watchDir, err := ioutil.TempDir("", "")
  2400  	require.Nil(t, err)
  2401  	dataDir1 := path.Join(watchDir, "data1")
  2402  	err = os.Mkdir(dataDir1, 0o777)
  2403  	require.Nil(t, err)
  2404  	realConfigFile := path.Join(dataDir1, "config.yaml")
  2405  	t.Logf("Real config file location: %s\n", realConfigFile)
  2406  	err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0o640)
  2407  	require.Nil(t, err)
  2408  	cleanup := func() {
  2409  		os.RemoveAll(watchDir)
  2410  	}
  2411  	// now, symlink the tm `data1` dir to `data` in the baseDir
  2412  	os.Symlink(dataDir1, path.Join(watchDir, "data"))
  2413  	// and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml`
  2414  	configFile := path.Join(watchDir, "config.yaml")
  2415  	os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile)
  2416  	t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml"))
  2417  	// init Viper
  2418  	v := New()
  2419  	v.SetConfigFile(configFile)
  2420  	err = v.ReadInConfig()
  2421  	require.Nil(t, err)
  2422  	require.Equal(t, "bar", v.Get("foo"))
  2423  	return v, watchDir, configFile, cleanup
  2424  }
  2425  
  2426  func TestWatchFile(t *testing.T) {
  2427  	if runtime.GOOS == "linux" {
  2428  		// TODO(bep) FIX ME
  2429  		t.Skip("Skip test on Linux ...")
  2430  	}
  2431  
  2432  	t.Run("file content changed", func(t *testing.T) {
  2433  		// given a `config.yaml` file being watched
  2434  		v, configFile, cleanup := newViperWithConfigFile(t)
  2435  		defer cleanup()
  2436  		_, err := os.Stat(configFile)
  2437  		require.NoError(t, err)
  2438  		t.Logf("test config file: %s\n", configFile)
  2439  		wg := sync.WaitGroup{}
  2440  		wg.Add(1)
  2441  		var wgDoneOnce sync.Once // OnConfigChange is called twice on Windows
  2442  		v.OnConfigChange(func(in fsnotify.Event) {
  2443  			t.Logf("config file changed")
  2444  			wgDoneOnce.Do(func() {
  2445  				wg.Done()
  2446  			})
  2447  		})
  2448  		v.WatchConfig()
  2449  		// when overwriting the file and waiting for the custom change notification handler to be triggered
  2450  		err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0o640)
  2451  		wg.Wait()
  2452  		// then the config value should have changed
  2453  		require.Nil(t, err)
  2454  		assert.Equal(t, "baz", v.Get("foo"))
  2455  	})
  2456  
  2457  	t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) {
  2458  		// skip if not executed on Linux
  2459  		if runtime.GOOS != "linux" {
  2460  			t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...")
  2461  		}
  2462  		v, watchDir, _, _ := newViperWithSymlinkedConfigFile(t)
  2463  		// defer cleanup()
  2464  		wg := sync.WaitGroup{}
  2465  		v.WatchConfig()
  2466  		v.OnConfigChange(func(in fsnotify.Event) {
  2467  			t.Logf("config file changed")
  2468  			wg.Done()
  2469  		})
  2470  		wg.Add(1)
  2471  		// when link to another `config.yaml` file
  2472  		dataDir2 := path.Join(watchDir, "data2")
  2473  		err := os.Mkdir(dataDir2, 0o777)
  2474  		require.Nil(t, err)
  2475  		configFile2 := path.Join(dataDir2, "config.yaml")
  2476  		err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0o640)
  2477  		require.Nil(t, err)
  2478  		// change the symlink using the `ln -sfn` command
  2479  		err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
  2480  		require.Nil(t, err)
  2481  		wg.Wait()
  2482  		// then
  2483  		require.Nil(t, err)
  2484  		assert.Equal(t, "baz", v.Get("foo"))
  2485  	})
  2486  }
  2487  
  2488  func TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) {
  2489  	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  2490  	flags.String("foo.bar", "cobra_flag", "")
  2491  
  2492  	v := New()
  2493  	assert.NoError(t, v.BindPFlags(flags))
  2494  
  2495  	config := &struct {
  2496  		Foo struct {
  2497  			Bar string
  2498  		}
  2499  	}{}
  2500  
  2501  	assert.NoError(t, v.Unmarshal(config))
  2502  	assert.Equal(t, "cobra_flag", config.Foo.Bar)
  2503  }
  2504  
  2505  // var yamlExampleWithDot = []byte(`Hacker: true
  2506  // name: steve
  2507  // hobbies:
  2508  //     - skateboarding
  2509  //     - snowboarding
  2510  //     - go
  2511  // clothing:
  2512  //     jacket: leather
  2513  //     trousers: denim
  2514  //     pants:
  2515  //         size: large
  2516  // age: 35
  2517  // eyes : brown
  2518  // beard: true
  2519  // emails:
  2520  //     steve@hacker.com:
  2521  //         created: 01/02/03
  2522  //         active: true
  2523  // `)
  2524  
  2525  func TestKeyDelimiter(t *testing.T) {
  2526  	v := NewWithOptions(KeyDelimiter("::"))
  2527  	v.SetConfigType("yaml")
  2528  	r := strings.NewReader(string(yamlExampleWithDot))
  2529  
  2530  	err := v.unmarshalReader(r, v.config)
  2531  	require.NoError(t, err)
  2532  
  2533  	values := map[string]interface{}{
  2534  		"image": map[string]interface{}{
  2535  			"repository": "someImage",
  2536  			"tag":        "1.0.0",
  2537  		},
  2538  		"ingress": map[string]interface{}{
  2539  			"annotations": map[string]interface{}{
  2540  				"traefik.frontend.rule.type":                 "PathPrefix",
  2541  				"traefik.ingress.kubernetes.io/ssl-redirect": "true",
  2542  			},
  2543  		},
  2544  	}
  2545  
  2546  	v.SetDefault("charts::values", values)
  2547  
  2548  	assert.Equal(t, "leather", v.GetString("clothing::jacket"))
  2549  	assert.Equal(t, "01/02/03", v.GetString("emails::steve@hacker.com::created"))
  2550  
  2551  	type config struct {
  2552  		Charts struct {
  2553  			Values map[string]interface{}
  2554  		}
  2555  	}
  2556  
  2557  	expected := config{
  2558  		Charts: struct {
  2559  			Values map[string]interface{}
  2560  		}{
  2561  			Values: values,
  2562  		},
  2563  	}
  2564  
  2565  	var actual config
  2566  
  2567  	assert.NoError(t, v.Unmarshal(&actual))
  2568  
  2569  	assert.Equal(t, expected, actual)
  2570  }
  2571  
  2572  var yamlDeepNestedSlices = []byte(`TV:
  2573  - title: "The Expanse"
  2574    title_i18n:
  2575      USA: "The Expanse"
  2576      Japan: "エクスパンス -巨獣めざめる-"
  2577    seasons:
  2578    - first_released: "December 14, 2015"
  2579      episodes:
  2580      - title: "Dulcinea"
  2581        air_date: "December 14, 2015"
  2582      - title: "The Big Empty"
  2583        air_date: "December 15, 2015"
  2584      - title: "Remember the Cant"
  2585        air_date: "December 22, 2015"
  2586    - first_released: "February 1, 2017"
  2587      episodes:
  2588      - title: "Safe"
  2589        air_date: "February 1, 2017"
  2590      - title: "Doors & Corners"
  2591        air_date: "February 1, 2017"
  2592      - title: "Static"
  2593        air_date: "February 8, 2017"
  2594    episodes:
  2595      - ["Dulcinea", "The Big Empty", "Remember the Cant"]
  2596      - ["Safe", "Doors & Corners", "Static"]
  2597  `)
  2598  
  2599  func TestSliceIndexAccess(t *testing.T) {
  2600  	v.SetConfigType("yaml")
  2601  	r := strings.NewReader(string(yamlDeepNestedSlices))
  2602  
  2603  	err := v.unmarshalReader(r, v.config)
  2604  	require.NoError(t, err)
  2605  
  2606  	assert.Equal(t, "The Expanse", v.GetString("tv.0.title"))
  2607  	assert.Equal(t, "February 1, 2017", v.GetString("tv.0.seasons.1.first_released"))
  2608  	assert.Equal(t, "Static", v.GetString("tv.0.seasons.1.episodes.2.title"))
  2609  	assert.Equal(t, "December 15, 2015", v.GetString("tv.0.seasons.0.episodes.1.air_date"))
  2610  
  2611  	// Test nested keys with capital letters
  2612  	assert.Equal(t, "The Expanse", v.GetString("tv.0.title_i18n.USA"))
  2613  	assert.Equal(t, "エクスパンス -巨獣めざめる-", v.GetString("tv.0.title_i18n.Japan"))
  2614  
  2615  	// Test for index out of bounds
  2616  	assert.Equal(t, "", v.GetString("tv.0.seasons.2.first_released"))
  2617  
  2618  	// Accessing multidimensional arrays
  2619  	assert.Equal(t, "Static", v.GetString("tv.0.episodes.1.2"))
  2620  }
  2621  
  2622  func BenchmarkGetBool(b *testing.B) {
  2623  	key := "BenchmarkGetBool"
  2624  	v = New()
  2625  	v.Set(key, true)
  2626  
  2627  	for i := 0; i < b.N; i++ {
  2628  		if !v.GetBool(key) {
  2629  			b.Fatal("GetBool returned false")
  2630  		}
  2631  	}
  2632  }
  2633  
  2634  func BenchmarkGet(b *testing.B) {
  2635  	key := "BenchmarkGet"
  2636  	v = New()
  2637  	v.Set(key, true)
  2638  
  2639  	for i := 0; i < b.N; i++ {
  2640  		if !v.Get(key).(bool) {
  2641  			b.Fatal("Get returned false")
  2642  		}
  2643  	}
  2644  }
  2645  
  2646  // BenchmarkGetBoolFromMap is the "perfect result" for the above.
  2647  func BenchmarkGetBoolFromMap(b *testing.B) {
  2648  	m := make(map[string]bool)
  2649  	key := "BenchmarkGetBool"
  2650  	m[key] = true
  2651  
  2652  	for i := 0; i < b.N; i++ {
  2653  		if !m[key] {
  2654  			b.Fatal("Map value was false")
  2655  		}
  2656  	}
  2657  }
  2658  
  2659  // Skip some tests on Windows that kept failing when Windows was added to the CI as a target.
  2660  func skipWindows(t *testing.T) {
  2661  	if runtime.GOOS == "windows" {
  2662  		t.Skip("Skip test on Windows")
  2663  	}
  2664  }