github.com/xl1605368195/viper@v1.1.8/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" //nolint:staticcheck
    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/xl1605368195/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  	RegisterAlias("Baz", "Roo")
   896  	RegisterAlias("Roo", "baz")
   897  }
   898  
   899  func TestUnmarshal(t *testing.T) {
   900  	SetDefault("port", 1313)
   901  	Set("name", "Steve")
   902  	Set("duration", "1s1ms")
   903  	Set("modes", []int{1, 2, 3})
   904  
   905  	type config struct {
   906  		Port     int
   907  		Name     string
   908  		Duration time.Duration
   909  		Modes    []int
   910  	}
   911  
   912  	var C config
   913  
   914  	err := Unmarshal(&C)
   915  	if err != nil {
   916  		t.Fatalf("unable to decode into struct, %v", err)
   917  	}
   918  
   919  	assert.Equal(
   920  		t,
   921  		&config{
   922  			Name:     "Steve",
   923  			Port:     1313,
   924  			Duration: time.Second + time.Millisecond,
   925  			Modes:    []int{1, 2, 3},
   926  		},
   927  		&C,
   928  	)
   929  
   930  	Set("port", 1234)
   931  	err = Unmarshal(&C)
   932  	if err != nil {
   933  		t.Fatalf("unable to decode into struct, %v", err)
   934  	}
   935  
   936  	assert.Equal(
   937  		t,
   938  		&config{
   939  			Name:     "Steve",
   940  			Port:     1234,
   941  			Duration: time.Second + time.Millisecond,
   942  			Modes:    []int{1, 2, 3},
   943  		},
   944  		&C,
   945  	)
   946  }
   947  
   948  func TestUnmarshalWithDecoderOptions(t *testing.T) {
   949  	Set("credentials", "{\"foo\":\"bar\"}")
   950  
   951  	opt := DecodeHook(mapstructure.ComposeDecodeHookFunc(
   952  		mapstructure.StringToTimeDurationHookFunc(),
   953  		mapstructure.StringToSliceHookFunc(","),
   954  		// Custom Decode Hook Function
   955  		func(rf reflect.Kind, rt reflect.Kind, data interface{}) (interface{}, error) {
   956  			if rf != reflect.String || rt != reflect.Map {
   957  				return data, nil
   958  			}
   959  			m := map[string]string{}
   960  			raw := data.(string)
   961  			if raw == "" {
   962  				return m, nil
   963  			}
   964  			return m, json.Unmarshal([]byte(raw), &m)
   965  		},
   966  	))
   967  
   968  	type config struct {
   969  		Credentials map[string]string
   970  	}
   971  
   972  	var C config
   973  
   974  	err := Unmarshal(&C, opt)
   975  	if err != nil {
   976  		t.Fatalf("unable to decode into struct, %v", err)
   977  	}
   978  
   979  	assert.Equal(t, &config{
   980  		Credentials: map[string]string{"foo": "bar"},
   981  	}, &C)
   982  }
   983  
   984  func TestBindPFlags(t *testing.T) {
   985  	v := New() // create independent Viper object
   986  	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
   987  
   988  	testValues := map[string]*string{
   989  		"host":     nil,
   990  		"port":     nil,
   991  		"endpoint": nil,
   992  	}
   993  
   994  	mutatedTestValues := map[string]string{
   995  		"host":     "localhost",
   996  		"port":     "6060",
   997  		"endpoint": "/public",
   998  	}
   999  
  1000  	for name := range testValues {
  1001  		testValues[name] = flagSet.String(name, "", "test")
  1002  	}
  1003  
  1004  	err := v.BindPFlags(flagSet)
  1005  	if err != nil {
  1006  		t.Fatalf("error binding flag set, %v", err)
  1007  	}
  1008  
  1009  	flagSet.VisitAll(func(flag *pflag.Flag) {
  1010  		flag.Value.Set(mutatedTestValues[flag.Name])
  1011  		flag.Changed = true
  1012  	})
  1013  
  1014  	for name, expected := range mutatedTestValues {
  1015  		assert.Equal(t, expected, v.Get(name))
  1016  	}
  1017  }
  1018  
  1019  //nolint:dupl
  1020  func TestBindPFlagsStringSlice(t *testing.T) {
  1021  	tests := []struct {
  1022  		Expected []string
  1023  		Value    string
  1024  	}{
  1025  		{[]string{}, ""},
  1026  		{[]string{"jeden"}, "jeden"},
  1027  		{[]string{"dwa", "trzy"}, "dwa,trzy"},
  1028  		{[]string{"cztery", "piec , szesc"}, "cztery,\"piec , szesc\""},
  1029  	}
  1030  
  1031  	v := New() // create independent Viper object
  1032  	defaultVal := []string{"default"}
  1033  	v.SetDefault("stringslice", defaultVal)
  1034  
  1035  	for _, testValue := range tests {
  1036  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1037  		flagSet.StringSlice("stringslice", testValue.Expected, "test")
  1038  
  1039  		for _, changed := range []bool{true, false} {
  1040  			flagSet.VisitAll(func(f *pflag.Flag) {
  1041  				f.Value.Set(testValue.Value)
  1042  				f.Changed = changed
  1043  			})
  1044  
  1045  			err := v.BindPFlags(flagSet)
  1046  			if err != nil {
  1047  				t.Fatalf("error binding flag set, %v", err)
  1048  			}
  1049  
  1050  			type TestStr struct {
  1051  				StringSlice []string
  1052  			}
  1053  			val := &TestStr{}
  1054  			if err := v.Unmarshal(val); err != nil {
  1055  				t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
  1056  			}
  1057  			if changed {
  1058  				assert.Equal(t, testValue.Expected, val.StringSlice)
  1059  				assert.Equal(t, testValue.Expected, v.Get("stringslice"))
  1060  			} else {
  1061  				assert.Equal(t, defaultVal, val.StringSlice)
  1062  			}
  1063  		}
  1064  	}
  1065  }
  1066  
  1067  //nolint:dupl
  1068  func TestBindPFlagsStringArray(t *testing.T) {
  1069  	tests := []struct {
  1070  		Expected []string
  1071  		Value    string
  1072  	}{
  1073  		{[]string{}, ""},
  1074  		{[]string{"jeden"}, "jeden"},
  1075  		{[]string{"dwa,trzy"}, "dwa,trzy"},
  1076  		{[]string{"cztery,\"piec , szesc\""}, "cztery,\"piec , szesc\""},
  1077  	}
  1078  
  1079  	v := New() // create independent Viper object
  1080  	defaultVal := []string{"default"}
  1081  	v.SetDefault("stringarray", defaultVal)
  1082  
  1083  	for _, testValue := range tests {
  1084  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1085  		flagSet.StringArray("stringarray", testValue.Expected, "test")
  1086  
  1087  		for _, changed := range []bool{true, false} {
  1088  			flagSet.VisitAll(func(f *pflag.Flag) {
  1089  				f.Value.Set(testValue.Value)
  1090  				f.Changed = changed
  1091  			})
  1092  
  1093  			err := v.BindPFlags(flagSet)
  1094  			if err != nil {
  1095  				t.Fatalf("error binding flag set, %v", err)
  1096  			}
  1097  
  1098  			type TestStr struct {
  1099  				StringArray []string
  1100  			}
  1101  			val := &TestStr{}
  1102  			if err := v.Unmarshal(val); err != nil {
  1103  				t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
  1104  			}
  1105  			if changed {
  1106  				assert.Equal(t, testValue.Expected, val.StringArray)
  1107  				assert.Equal(t, testValue.Expected, v.Get("stringarray"))
  1108  			} else {
  1109  				assert.Equal(t, defaultVal, val.StringArray)
  1110  			}
  1111  		}
  1112  	}
  1113  }
  1114  
  1115  func TestSliceFlagsReturnCorrectType(t *testing.T) {
  1116  	flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1117  	flagSet.IntSlice("int", []int{1, 2}, "")
  1118  	flagSet.StringSlice("str", []string{"3", "4"}, "")
  1119  	flagSet.DurationSlice("duration", []time.Duration{5 * time.Second}, "")
  1120  
  1121  	v := New()
  1122  	v.BindPFlags(flagSet)
  1123  
  1124  	all := v.AllSettings()
  1125  
  1126  	if _, ok := all["int"].([]int); !ok {
  1127  		t.Errorf("unexpected type %T expected []int", all["int"])
  1128  	}
  1129  	if _, ok := all["str"].([]string); !ok {
  1130  		t.Errorf("unexpected type %T expected []string", all["str"])
  1131  	}
  1132  	if _, ok := all["duration"].([]time.Duration); !ok {
  1133  		t.Errorf("unexpected type %T expected []time.Duration", all["duration"])
  1134  	}
  1135  }
  1136  
  1137  //nolint:dupl
  1138  func TestBindPFlagsIntSlice(t *testing.T) {
  1139  	tests := []struct {
  1140  		Expected []int
  1141  		Value    string
  1142  	}{
  1143  		{[]int{}, ""},
  1144  		{[]int{1}, "1"},
  1145  		{[]int{2, 3}, "2,3"},
  1146  	}
  1147  
  1148  	v := New() // create independent Viper object
  1149  	defaultVal := []int{0}
  1150  	v.SetDefault("intslice", defaultVal)
  1151  
  1152  	for _, testValue := range tests {
  1153  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1154  		flagSet.IntSlice("intslice", testValue.Expected, "test")
  1155  
  1156  		for _, changed := range []bool{true, false} {
  1157  			flagSet.VisitAll(func(f *pflag.Flag) {
  1158  				f.Value.Set(testValue.Value)
  1159  				f.Changed = changed
  1160  			})
  1161  
  1162  			err := v.BindPFlags(flagSet)
  1163  			if err != nil {
  1164  				t.Fatalf("error binding flag set, %v", err)
  1165  			}
  1166  
  1167  			type TestInt struct {
  1168  				IntSlice []int
  1169  			}
  1170  			val := &TestInt{}
  1171  			if err := v.Unmarshal(val); err != nil {
  1172  				t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
  1173  			}
  1174  			if changed {
  1175  				assert.Equal(t, testValue.Expected, val.IntSlice)
  1176  				assert.Equal(t, testValue.Expected, v.Get("intslice"))
  1177  			} else {
  1178  				assert.Equal(t, defaultVal, val.IntSlice)
  1179  			}
  1180  		}
  1181  	}
  1182  }
  1183  
  1184  func TestBindPFlag(t *testing.T) {
  1185  	testString := "testing"
  1186  	testValue := newStringValue(testString, &testString)
  1187  
  1188  	flag := &pflag.Flag{
  1189  		Name:    "testflag",
  1190  		Value:   testValue,
  1191  		Changed: false,
  1192  	}
  1193  
  1194  	BindPFlag("testvalue", flag)
  1195  
  1196  	assert.Equal(t, testString, Get("testvalue"))
  1197  
  1198  	flag.Value.Set("testing_mutate")
  1199  	flag.Changed = true // hack for pflag usage
  1200  
  1201  	assert.Equal(t, "testing_mutate", Get("testvalue"))
  1202  }
  1203  
  1204  func TestBindPFlagDetectNilFlag(t *testing.T) {
  1205  	result := BindPFlag("testvalue", nil)
  1206  	assert.Error(t, result)
  1207  }
  1208  
  1209  func TestBindPFlagStringToString(t *testing.T) {
  1210  	tests := []struct {
  1211  		Expected map[string]string
  1212  		Value    string
  1213  	}{
  1214  		{map[string]string{}, ""},
  1215  		{map[string]string{"yo": "hi"}, "yo=hi"},
  1216  		{map[string]string{"yo": "hi", "oh": "hi=there"}, "yo=hi,oh=hi=there"},
  1217  		{map[string]string{"yo": ""}, "yo="},
  1218  		{map[string]string{"yo": "", "oh": "hi=there"}, "yo=,oh=hi=there"},
  1219  	}
  1220  
  1221  	v := New() // create independent Viper object
  1222  	defaultVal := map[string]string{}
  1223  	v.SetDefault("stringtostring", defaultVal)
  1224  
  1225  	for _, testValue := range tests {
  1226  		flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
  1227  		flagSet.StringToString("stringtostring", testValue.Expected, "test")
  1228  
  1229  		for _, changed := range []bool{true, false} {
  1230  			flagSet.VisitAll(func(f *pflag.Flag) {
  1231  				f.Value.Set(testValue.Value)
  1232  				f.Changed = changed
  1233  			})
  1234  
  1235  			err := v.BindPFlags(flagSet)
  1236  			if err != nil {
  1237  				t.Fatalf("error binding flag set, %v", err)
  1238  			}
  1239  
  1240  			type TestMap struct {
  1241  				StringToString map[string]string
  1242  			}
  1243  			val := &TestMap{}
  1244  			if err := v.Unmarshal(val); err != nil {
  1245  				t.Fatalf("%+#v cannot unmarshal: %s", testValue.Value, err)
  1246  			}
  1247  			if changed {
  1248  				assert.Equal(t, testValue.Expected, val.StringToString)
  1249  			} else {
  1250  				assert.Equal(t, defaultVal, val.StringToString)
  1251  			}
  1252  		}
  1253  	}
  1254  }
  1255  
  1256  func TestBoundCaseSensitivity(t *testing.T) {
  1257  	assert.Equal(t, "brown", Get("eyes"))
  1258  
  1259  	BindEnv("eYEs", "TURTLE_EYES")
  1260  
  1261  	testutil.Setenv(t, "TURTLE_EYES", "blue")
  1262  
  1263  	assert.Equal(t, "blue", Get("eyes"))
  1264  
  1265  	testString := "green"
  1266  	testValue := newStringValue(testString, &testString)
  1267  
  1268  	flag := &pflag.Flag{
  1269  		Name:    "eyeballs",
  1270  		Value:   testValue,
  1271  		Changed: true,
  1272  	}
  1273  
  1274  	BindPFlag("eYEs", flag)
  1275  	assert.Equal(t, "green", Get("eyes"))
  1276  }
  1277  
  1278  func TestSizeInBytes(t *testing.T) {
  1279  	input := map[string]uint{
  1280  		"":               0,
  1281  		"b":              0,
  1282  		"12 bytes":       0,
  1283  		"200000000000gb": 0,
  1284  		"12 b":           12,
  1285  		"43 MB":          43 * (1 << 20),
  1286  		"10mb":           10 * (1 << 20),
  1287  		"1gb":            1 << 30,
  1288  	}
  1289  
  1290  	for str, expected := range input {
  1291  		assert.Equal(t, expected, parseSizeInBytes(str), str)
  1292  	}
  1293  }
  1294  
  1295  func TestFindsNestedKeys(t *testing.T) {
  1296  	initConfigs()
  1297  	dob, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
  1298  
  1299  	Set("super", map[string]interface{}{
  1300  		"deep": map[string]interface{}{
  1301  			"nested": "value",
  1302  		},
  1303  	})
  1304  
  1305  	expected := map[string]interface{}{
  1306  		"super": map[string]interface{}{
  1307  			"deep": map[string]interface{}{
  1308  				"nested": "value",
  1309  			},
  1310  		},
  1311  		"super.deep": map[string]interface{}{
  1312  			"nested": "value",
  1313  		},
  1314  		"super.deep.nested":  "value",
  1315  		"owner.organization": "MongoDB",
  1316  		"batters.batter": []interface{}{
  1317  			map[string]interface{}{
  1318  				"type": "Regular",
  1319  			},
  1320  			map[string]interface{}{
  1321  				"type": "Chocolate",
  1322  			},
  1323  			map[string]interface{}{
  1324  				"type": "Blueberry",
  1325  			},
  1326  			map[string]interface{}{
  1327  				"type": "Devil's Food",
  1328  			},
  1329  		},
  1330  		"hobbies": []interface{}{
  1331  			"skateboarding", "snowboarding", "go",
  1332  		},
  1333  		"TITLE_DOTENV": "DotEnv Example",
  1334  		"TYPE_DOTENV":  "donut",
  1335  		"NAME_DOTENV":  "Cake",
  1336  		"title":        "TOML Example",
  1337  		"newkey":       "remote",
  1338  		"batters": map[string]interface{}{
  1339  			"batter": []interface{}{
  1340  				map[string]interface{}{
  1341  					"type": "Regular",
  1342  				},
  1343  				map[string]interface{}{
  1344  					"type": "Chocolate",
  1345  				},
  1346  				map[string]interface{}{
  1347  					"type": "Blueberry",
  1348  				},
  1349  				map[string]interface{}{
  1350  					"type": "Devil's Food",
  1351  				},
  1352  			},
  1353  		},
  1354  		"eyes": "brown",
  1355  		"age":  35,
  1356  		"owner": map[string]interface{}{
  1357  			"organization": "MongoDB",
  1358  			"bio":          "MongoDB Chief Developer Advocate & Hacker at Large",
  1359  			"dob":          dob,
  1360  		},
  1361  		"owner.bio": "MongoDB Chief Developer Advocate & Hacker at Large",
  1362  		"type":      "donut",
  1363  		"id":        "0001",
  1364  		"name":      "Cake",
  1365  		"hacker":    true,
  1366  		"ppu":       0.55,
  1367  		"clothing": map[string]interface{}{
  1368  			"jacket":   "leather",
  1369  			"trousers": "denim",
  1370  			"pants": map[string]interface{}{
  1371  				"size": "large",
  1372  			},
  1373  		},
  1374  		"clothing.jacket":     "leather",
  1375  		"clothing.pants.size": "large",
  1376  		"clothing.trousers":   "denim",
  1377  		"owner.dob":           dob,
  1378  		"beard":               true,
  1379  		"foos": []map[string]interface{}{
  1380  			{
  1381  				"foo": []map[string]interface{}{
  1382  					{
  1383  						"key": 1,
  1384  					},
  1385  					{
  1386  						"key": 2,
  1387  					},
  1388  					{
  1389  						"key": 3,
  1390  					},
  1391  					{
  1392  						"key": 4,
  1393  					},
  1394  				},
  1395  			},
  1396  		},
  1397  	}
  1398  
  1399  	for key, expectedValue := range expected {
  1400  		assert.Equal(t, expectedValue, v.Get(key))
  1401  	}
  1402  }
  1403  
  1404  func TestReadBufConfig(t *testing.T) {
  1405  	v := New()
  1406  	v.SetConfigType("yaml")
  1407  	v.ReadConfig(bytes.NewBuffer(yamlExample))
  1408  	t.Log(v.AllKeys())
  1409  
  1410  	assert.True(t, v.InConfig("name"))
  1411  	assert.True(t, v.InConfig("clothing.jacket"))
  1412  	assert.False(t, v.InConfig("state"))
  1413  	assert.False(t, v.InConfig("clothing.hat"))
  1414  	assert.Equal(t, "steve", v.Get("name"))
  1415  	assert.Equal(t, []interface{}{"skateboarding", "snowboarding", "go"}, v.Get("hobbies"))
  1416  	assert.Equal(t, map[string]interface{}{"jacket": "leather", "trousers": "denim", "pants": map[string]interface{}{"size": "large"}}, v.Get("clothing"))
  1417  	assert.Equal(t, 35, v.Get("age"))
  1418  }
  1419  
  1420  func TestIsSet(t *testing.T) {
  1421  	v := New()
  1422  	v.SetConfigType("yaml")
  1423  
  1424  	/* config and defaults */
  1425  	v.ReadConfig(bytes.NewBuffer(yamlExample))
  1426  	v.SetDefault("clothing.shoes", "sneakers")
  1427  
  1428  	assert.True(t, v.IsSet("clothing"))
  1429  	assert.True(t, v.IsSet("clothing.jacket"))
  1430  	assert.False(t, v.IsSet("clothing.jackets"))
  1431  	assert.True(t, v.IsSet("clothing.shoes"))
  1432  
  1433  	/* state change */
  1434  	assert.False(t, v.IsSet("helloworld"))
  1435  	v.Set("helloworld", "fubar")
  1436  	assert.True(t, v.IsSet("helloworld"))
  1437  
  1438  	/* env */
  1439  	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
  1440  	v.BindEnv("eyes")
  1441  	v.BindEnv("foo")
  1442  	v.BindEnv("clothing.hat")
  1443  	v.BindEnv("clothing.hats")
  1444  
  1445  	testutil.Setenv(t, "FOO", "bar")
  1446  	testutil.Setenv(t, "CLOTHING_HAT", "bowler")
  1447  
  1448  	assert.True(t, v.IsSet("eyes"))           // in the config file
  1449  	assert.True(t, v.IsSet("foo"))            // in the environment
  1450  	assert.True(t, v.IsSet("clothing.hat"))   // in the environment
  1451  	assert.False(t, v.IsSet("clothing.hats")) // not defined
  1452  
  1453  	/* flags */
  1454  	flagset := pflag.NewFlagSet("testisset", pflag.ContinueOnError)
  1455  	flagset.Bool("foobaz", false, "foobaz")
  1456  	flagset.Bool("barbaz", false, "barbaz")
  1457  	foobaz, barbaz := flagset.Lookup("foobaz"), flagset.Lookup("barbaz")
  1458  	v.BindPFlag("foobaz", foobaz)
  1459  	v.BindPFlag("barbaz", barbaz)
  1460  	barbaz.Value.Set("true")
  1461  	barbaz.Changed = true // hack for pflag usage
  1462  
  1463  	assert.False(t, v.IsSet("foobaz"))
  1464  	assert.True(t, v.IsSet("barbaz"))
  1465  }
  1466  
  1467  func TestDirsSearch(t *testing.T) {
  1468  	root, config, cleanup := initDirs(t)
  1469  	defer cleanup()
  1470  
  1471  	v := New()
  1472  	v.SetConfigName(config)
  1473  	v.SetDefault(`key`, `default`)
  1474  
  1475  	entries, err := ioutil.ReadDir(root)
  1476  	assert.Nil(t, err)
  1477  	for _, e := range entries {
  1478  		if e.IsDir() {
  1479  			v.AddConfigPath(e.Name())
  1480  		}
  1481  	}
  1482  
  1483  	err = v.ReadInConfig()
  1484  	assert.Nil(t, err)
  1485  
  1486  	assert.Equal(t, `value is `+filepath.Base(v.configPaths[0]), v.GetString(`key`))
  1487  }
  1488  
  1489  func TestWrongDirsSearchNotFound(t *testing.T) {
  1490  	_, config, cleanup := initDirs(t)
  1491  	defer cleanup()
  1492  
  1493  	v := New()
  1494  	v.SetConfigName(config)
  1495  	v.SetDefault(`key`, `default`)
  1496  
  1497  	v.AddConfigPath(`whattayoutalkingbout`)
  1498  	v.AddConfigPath(`thispathaintthere`)
  1499  
  1500  	err := v.ReadInConfig()
  1501  	assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
  1502  
  1503  	// Even though config did not load and the error might have
  1504  	// been ignored by the client, the default still loads
  1505  	assert.Equal(t, `default`, v.GetString(`key`))
  1506  }
  1507  
  1508  func TestWrongDirsSearchNotFoundForMerge(t *testing.T) {
  1509  	_, config, cleanup := initDirs(t)
  1510  	defer cleanup()
  1511  
  1512  	v := New()
  1513  	v.SetConfigName(config)
  1514  	v.SetDefault(`key`, `default`)
  1515  
  1516  	v.AddConfigPath(`whattayoutalkingbout`)
  1517  	v.AddConfigPath(`thispathaintthere`)
  1518  
  1519  	err := v.MergeInConfig()
  1520  	assert.Equal(t, reflect.TypeOf(ConfigFileNotFoundError{"", ""}), reflect.TypeOf(err))
  1521  
  1522  	// Even though config did not load and the error might have
  1523  	// been ignored by the client, the default still loads
  1524  	assert.Equal(t, `default`, v.GetString(`key`))
  1525  }
  1526  
  1527  func TestSub(t *testing.T) {
  1528  	v := New()
  1529  	v.SetConfigType("yaml")
  1530  	v.ReadConfig(bytes.NewBuffer(yamlExample))
  1531  
  1532  	subv := v.Sub("clothing")
  1533  	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("pants.size"))
  1534  
  1535  	subv = v.Sub("clothing.pants")
  1536  	assert.Equal(t, v.Get("clothing.pants.size"), subv.Get("size"))
  1537  
  1538  	subv = v.Sub("clothing.pants.size")
  1539  	assert.Equal(t, (*Viper)(nil), subv)
  1540  
  1541  	subv = v.Sub("missing.key")
  1542  	assert.Equal(t, (*Viper)(nil), subv)
  1543  
  1544  	subv = v.Sub("clothing")
  1545  	assert.Equal(t, subv.parents[0], "clothing")
  1546  
  1547  	subv = v.Sub("clothing").Sub("pants")
  1548  	assert.Equal(t, len(subv.parents), 2)
  1549  	assert.Equal(t, subv.parents[0], "clothing")
  1550  	assert.Equal(t, subv.parents[1], "pants")
  1551  }
  1552  
  1553  var hclWriteExpected = []byte(`"foos" = {
  1554    "foo" = {
  1555      "key" = 1
  1556    }
  1557  
  1558    "foo" = {
  1559      "key" = 2
  1560    }
  1561  
  1562    "foo" = {
  1563      "key" = 3
  1564    }
  1565  
  1566    "foo" = {
  1567      "key" = 4
  1568    }
  1569  }
  1570  
  1571  "id" = "0001"
  1572  
  1573  "name" = "Cake"
  1574  
  1575  "ppu" = 0.55
  1576  
  1577  "type" = "donut"`)
  1578  
  1579  var jsonWriteExpected = []byte(`{
  1580    "batters": {
  1581      "batter": [
  1582        {
  1583          "type": "Regular"
  1584        },
  1585        {
  1586          "type": "Chocolate"
  1587        },
  1588        {
  1589          "type": "Blueberry"
  1590        },
  1591        {
  1592          "type": "Devil's Food"
  1593        }
  1594      ]
  1595    },
  1596    "id": "0001",
  1597    "name": "Cake",
  1598    "ppu": 0.55,
  1599    "type": "donut"
  1600  }`)
  1601  
  1602  var propertiesWriteExpected = []byte(`p_id = 0001
  1603  p_type = donut
  1604  p_name = Cake
  1605  p_ppu = 0.55
  1606  p_batters.batter.type = Regular
  1607  `)
  1608  
  1609  // var yamlWriteExpected = []byte(`age: 35
  1610  // beard: true
  1611  // clothing:
  1612  //     jacket: leather
  1613  //     pants:
  1614  //         size: large
  1615  //     trousers: denim
  1616  // eyes: brown
  1617  // hacker: true
  1618  // hobbies:
  1619  //     - skateboarding
  1620  //     - snowboarding
  1621  //     - go
  1622  // name: steve
  1623  // `)
  1624  
  1625  func TestWriteConfig(t *testing.T) {
  1626  	fs := afero.NewMemMapFs()
  1627  	testCases := map[string]struct {
  1628  		configName      string
  1629  		inConfigType    string
  1630  		outConfigType   string
  1631  		fileName        string
  1632  		input           []byte
  1633  		expectedContent []byte
  1634  	}{
  1635  		"hcl with file extension": {
  1636  			configName:      "c",
  1637  			inConfigType:    "hcl",
  1638  			outConfigType:   "hcl",
  1639  			fileName:        "c.hcl",
  1640  			input:           hclExample,
  1641  			expectedContent: hclWriteExpected,
  1642  		},
  1643  		"hcl without file extension": {
  1644  			configName:      "c",
  1645  			inConfigType:    "hcl",
  1646  			outConfigType:   "hcl",
  1647  			fileName:        "c",
  1648  			input:           hclExample,
  1649  			expectedContent: hclWriteExpected,
  1650  		},
  1651  		"hcl with file extension and mismatch type": {
  1652  			configName:      "c",
  1653  			inConfigType:    "hcl",
  1654  			outConfigType:   "json",
  1655  			fileName:        "c.hcl",
  1656  			input:           hclExample,
  1657  			expectedContent: hclWriteExpected,
  1658  		},
  1659  		"json with file extension": {
  1660  			configName:      "c",
  1661  			inConfigType:    "json",
  1662  			outConfigType:   "json",
  1663  			fileName:        "c.json",
  1664  			input:           jsonExample,
  1665  			expectedContent: jsonWriteExpected,
  1666  		},
  1667  		"json without file extension": {
  1668  			configName:      "c",
  1669  			inConfigType:    "json",
  1670  			outConfigType:   "json",
  1671  			fileName:        "c",
  1672  			input:           jsonExample,
  1673  			expectedContent: jsonWriteExpected,
  1674  		},
  1675  		"json with file extension and mismatch type": {
  1676  			configName:      "c",
  1677  			inConfigType:    "json",
  1678  			outConfigType:   "hcl",
  1679  			fileName:        "c.json",
  1680  			input:           jsonExample,
  1681  			expectedContent: jsonWriteExpected,
  1682  		},
  1683  		"properties with file extension": {
  1684  			configName:      "c",
  1685  			inConfigType:    "properties",
  1686  			outConfigType:   "properties",
  1687  			fileName:        "c.properties",
  1688  			input:           propertiesExample,
  1689  			expectedContent: propertiesWriteExpected,
  1690  		},
  1691  		"properties without file extension": {
  1692  			configName:      "c",
  1693  			inConfigType:    "properties",
  1694  			outConfigType:   "properties",
  1695  			fileName:        "c",
  1696  			input:           propertiesExample,
  1697  			expectedContent: propertiesWriteExpected,
  1698  		},
  1699  		"yaml with file extension": {
  1700  			configName:      "c",
  1701  			inConfigType:    "yaml",
  1702  			outConfigType:   "yaml",
  1703  			fileName:        "c.yaml",
  1704  			input:           yamlExample,
  1705  			expectedContent: yamlWriteExpected,
  1706  		},
  1707  		"yaml without file extension": {
  1708  			configName:      "c",
  1709  			inConfigType:    "yaml",
  1710  			outConfigType:   "yaml",
  1711  			fileName:        "c",
  1712  			input:           yamlExample,
  1713  			expectedContent: yamlWriteExpected,
  1714  		},
  1715  		"yaml with file extension and mismatch type": {
  1716  			configName:      "c",
  1717  			inConfigType:    "yaml",
  1718  			outConfigType:   "json",
  1719  			fileName:        "c.yaml",
  1720  			input:           yamlExample,
  1721  			expectedContent: yamlWriteExpected,
  1722  		},
  1723  	}
  1724  	for name, tc := range testCases {
  1725  		t.Run(name, func(t *testing.T) {
  1726  			v := New()
  1727  			v.SetFs(fs)
  1728  			v.SetConfigName(tc.fileName)
  1729  			v.SetConfigType(tc.inConfigType)
  1730  
  1731  			err := v.ReadConfig(bytes.NewBuffer(tc.input))
  1732  			if err != nil {
  1733  				t.Fatal(err)
  1734  			}
  1735  			v.SetConfigType(tc.outConfigType)
  1736  			if err := v.WriteConfigAs(tc.fileName); err != nil {
  1737  				t.Fatal(err)
  1738  			}
  1739  			read, err := afero.ReadFile(fs, tc.fileName)
  1740  			if err != nil {
  1741  				t.Fatal(err)
  1742  			}
  1743  			assert.Equal(t, tc.expectedContent, read)
  1744  		})
  1745  	}
  1746  }
  1747  
  1748  func TestWriteConfigTOML(t *testing.T) {
  1749  	fs := afero.NewMemMapFs()
  1750  
  1751  	testCases := map[string]struct {
  1752  		configName string
  1753  		configType string
  1754  		fileName   string
  1755  		input      []byte
  1756  	}{
  1757  		"with file extension": {
  1758  			configName: "c",
  1759  			configType: "toml",
  1760  			fileName:   "c.toml",
  1761  			input:      tomlExample,
  1762  		},
  1763  		"without file extension": {
  1764  			configName: "c",
  1765  			configType: "toml",
  1766  			fileName:   "c",
  1767  			input:      tomlExample,
  1768  		},
  1769  	}
  1770  	for name, tc := range testCases {
  1771  		t.Run(name, func(t *testing.T) {
  1772  			v := New()
  1773  			v.SetFs(fs)
  1774  			v.SetConfigName(tc.configName)
  1775  			v.SetConfigType(tc.configType)
  1776  			err := v.ReadConfig(bytes.NewBuffer(tc.input))
  1777  			if err != nil {
  1778  				t.Fatal(err)
  1779  			}
  1780  			if err := v.WriteConfigAs(tc.fileName); err != nil {
  1781  				t.Fatal(err)
  1782  			}
  1783  
  1784  			// The TOML String method does not order the contents.
  1785  			// Therefore, we must read the generated file and compare the data.
  1786  			v2 := New()
  1787  			v2.SetFs(fs)
  1788  			v2.SetConfigName(tc.configName)
  1789  			v2.SetConfigType(tc.configType)
  1790  			v2.SetConfigFile(tc.fileName)
  1791  			err = v2.ReadInConfig()
  1792  			if err != nil {
  1793  				t.Fatal(err)
  1794  			}
  1795  
  1796  			assert.Equal(t, v.GetString("title"), v2.GetString("title"))
  1797  			assert.Equal(t, v.GetString("owner.bio"), v2.GetString("owner.bio"))
  1798  			assert.Equal(t, v.GetString("owner.dob"), v2.GetString("owner.dob"))
  1799  			assert.Equal(t, v.GetString("owner.organization"), v2.GetString("owner.organization"))
  1800  		})
  1801  	}
  1802  }
  1803  
  1804  func TestWriteConfigDotEnv(t *testing.T) {
  1805  	fs := afero.NewMemMapFs()
  1806  	testCases := map[string]struct {
  1807  		configName string
  1808  		configType string
  1809  		fileName   string
  1810  		input      []byte
  1811  	}{
  1812  		"with file extension": {
  1813  			configName: "c",
  1814  			configType: "env",
  1815  			fileName:   "c.env",
  1816  			input:      dotenvExample,
  1817  		},
  1818  		"without file extension": {
  1819  			configName: "c",
  1820  			configType: "env",
  1821  			fileName:   "c",
  1822  			input:      dotenvExample,
  1823  		},
  1824  	}
  1825  	for name, tc := range testCases {
  1826  		t.Run(name, func(t *testing.T) {
  1827  			v := New()
  1828  			v.SetFs(fs)
  1829  			v.SetConfigName(tc.configName)
  1830  			v.SetConfigType(tc.configType)
  1831  			err := v.ReadConfig(bytes.NewBuffer(tc.input))
  1832  			if err != nil {
  1833  				t.Fatal(err)
  1834  			}
  1835  			if err := v.WriteConfigAs(tc.fileName); err != nil {
  1836  				t.Fatal(err)
  1837  			}
  1838  
  1839  			// The TOML String method does not order the contents.
  1840  			// Therefore, we must read the generated file and compare the data.
  1841  			v2 := New()
  1842  			v2.SetFs(fs)
  1843  			v2.SetConfigName(tc.configName)
  1844  			v2.SetConfigType(tc.configType)
  1845  			v2.SetConfigFile(tc.fileName)
  1846  			err = v2.ReadInConfig()
  1847  			if err != nil {
  1848  				t.Fatal(err)
  1849  			}
  1850  
  1851  			assert.Equal(t, v.GetString("title_dotenv"), v2.GetString("title_dotenv"))
  1852  			assert.Equal(t, v.GetString("type_dotenv"), v2.GetString("type_dotenv"))
  1853  			assert.Equal(t, v.GetString("kind_dotenv"), v2.GetString("kind_dotenv"))
  1854  		})
  1855  	}
  1856  }
  1857  
  1858  func TestSafeWriteConfig(t *testing.T) {
  1859  	v := New()
  1860  	fs := afero.NewMemMapFs()
  1861  	v.SetFs(fs)
  1862  	v.AddConfigPath("/test")
  1863  	v.SetConfigName("c")
  1864  	v.SetConfigType("yaml")
  1865  	require.NoError(t, v.ReadConfig(bytes.NewBuffer(yamlExample)))
  1866  	require.NoError(t, v.SafeWriteConfig())
  1867  	read, err := afero.ReadFile(fs, testutil.AbsFilePath(t, "/test/c.yaml"))
  1868  	require.NoError(t, err)
  1869  	assert.Equal(t, yamlWriteExpected, read)
  1870  }
  1871  
  1872  func TestSafeWriteConfigWithMissingConfigPath(t *testing.T) {
  1873  	v := New()
  1874  	fs := afero.NewMemMapFs()
  1875  	v.SetFs(fs)
  1876  	v.SetConfigName("c")
  1877  	v.SetConfigType("yaml")
  1878  	require.EqualError(t, v.SafeWriteConfig(), "missing configuration for 'configPath'")
  1879  }
  1880  
  1881  func TestSafeWriteConfigWithExistingFile(t *testing.T) {
  1882  	v := New()
  1883  	fs := afero.NewMemMapFs()
  1884  	fs.Create(testutil.AbsFilePath(t, "/test/c.yaml"))
  1885  	v.SetFs(fs)
  1886  	v.AddConfigPath("/test")
  1887  	v.SetConfigName("c")
  1888  	v.SetConfigType("yaml")
  1889  	err := v.SafeWriteConfig()
  1890  	require.Error(t, err)
  1891  	_, ok := err.(ConfigFileAlreadyExistsError)
  1892  	assert.True(t, ok, "Expected ConfigFileAlreadyExistsError")
  1893  }
  1894  
  1895  func TestSafeWriteAsConfig(t *testing.T) {
  1896  	v := New()
  1897  	fs := afero.NewMemMapFs()
  1898  	v.SetFs(fs)
  1899  	err := v.ReadConfig(bytes.NewBuffer(yamlExample))
  1900  	if err != nil {
  1901  		t.Fatal(err)
  1902  	}
  1903  	require.NoError(t, v.SafeWriteConfigAs("/test/c.yaml"))
  1904  	if _, err = afero.ReadFile(fs, "/test/c.yaml"); err != nil {
  1905  		t.Fatal(err)
  1906  	}
  1907  }
  1908  
  1909  func TestSafeWriteConfigAsWithExistingFile(t *testing.T) {
  1910  	v := New()
  1911  	fs := afero.NewMemMapFs()
  1912  	fs.Create("/test/c.yaml")
  1913  	v.SetFs(fs)
  1914  	err := v.SafeWriteConfigAs("/test/c.yaml")
  1915  	require.Error(t, err)
  1916  	_, ok := err.(ConfigFileAlreadyExistsError)
  1917  	assert.True(t, ok, "Expected ConfigFileAlreadyExistsError")
  1918  }
  1919  
  1920  func TestWriteHiddenFile(t *testing.T) {
  1921  	v := New()
  1922  	fs := afero.NewMemMapFs()
  1923  	fs.Create(testutil.AbsFilePath(t, "/test/.config"))
  1924  	v.SetFs(fs)
  1925  
  1926  	v.SetConfigName(".config")
  1927  	v.SetConfigType("yaml")
  1928  	v.AddConfigPath("/test")
  1929  
  1930  	err := v.ReadInConfig()
  1931  	require.NoError(t, err)
  1932  
  1933  	err = v.WriteConfig()
  1934  	require.NoError(t, err)
  1935  }
  1936  
  1937  var yamlMergeExampleTgt = []byte(`
  1938  hello:
  1939      pop: 37890
  1940      largenum: 765432101234567
  1941      num2pow63: 9223372036854775808
  1942      universe: null
  1943      world:
  1944      - us
  1945      - uk
  1946      - fr
  1947      - de
  1948  `)
  1949  
  1950  var yamlMergeExampleSrc = []byte(`
  1951  hello:
  1952      pop: 45000
  1953      largenum: 7654321001234567
  1954      universe:
  1955      - mw
  1956      - ad
  1957      ints:
  1958      - 1
  1959      - 2
  1960  fu: bar
  1961  `)
  1962  
  1963  var jsonMergeExampleTgt = []byte(`
  1964  {
  1965  	"hello": {
  1966  		"foo": null,
  1967  		"pop": 123456
  1968  	}
  1969  }
  1970  `)
  1971  
  1972  var jsonMergeExampleSrc = []byte(`
  1973  {
  1974  	"hello": {
  1975  		"foo": "foo str",
  1976  		"pop": "pop str"
  1977  	}
  1978  }
  1979  `)
  1980  
  1981  func TestMergeConfig(t *testing.T) {
  1982  	v := New()
  1983  	v.SetConfigType("yml")
  1984  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  1985  		t.Fatal(err)
  1986  	}
  1987  
  1988  	if pop := v.GetInt("hello.pop"); pop != 37890 {
  1989  		t.Fatalf("pop != 37890, = %d", pop)
  1990  	}
  1991  
  1992  	if pop := v.GetInt32("hello.pop"); pop != int32(37890) {
  1993  		t.Fatalf("pop != 37890, = %d", pop)
  1994  	}
  1995  
  1996  	if pop := v.GetInt64("hello.largenum"); pop != int64(765432101234567) {
  1997  		t.Fatalf("int64 largenum != 765432101234567, = %d", pop)
  1998  	}
  1999  
  2000  	if pop := v.GetUint("hello.pop"); pop != 37890 {
  2001  		t.Fatalf("uint pop != 37890, = %d", pop)
  2002  	}
  2003  
  2004  	if pop := v.GetUint16("hello.pop"); pop != uint16(37890) {
  2005  		t.Fatalf("uint pop != 37890, = %d", pop)
  2006  	}
  2007  
  2008  	if pop := v.GetUint32("hello.pop"); pop != 37890 {
  2009  		t.Fatalf("uint32 pop != 37890, = %d", pop)
  2010  	}
  2011  
  2012  	if pop := v.GetUint64("hello.num2pow63"); pop != 9223372036854775808 {
  2013  		t.Fatalf("uint64 num2pow63 != 9223372036854775808, = %d", pop)
  2014  	}
  2015  
  2016  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  2017  		t.Fatalf("len(world) != 4, = %d", len(world))
  2018  	}
  2019  
  2020  	if fu := v.GetString("fu"); fu != "" {
  2021  		t.Fatalf("fu != \"\", = %s", fu)
  2022  	}
  2023  
  2024  	if err := v.MergeConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
  2025  		t.Fatal(err)
  2026  	}
  2027  
  2028  	if pop := v.GetInt("hello.pop"); pop != 45000 {
  2029  		t.Fatalf("pop != 45000, = %d", pop)
  2030  	}
  2031  
  2032  	if pop := v.GetInt32("hello.pop"); pop != int32(45000) {
  2033  		t.Fatalf("pop != 45000, = %d", pop)
  2034  	}
  2035  
  2036  	if pop := v.GetInt64("hello.largenum"); pop != int64(7654321001234567) {
  2037  		t.Fatalf("int64 largenum != 7654321001234567, = %d", pop)
  2038  	}
  2039  
  2040  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  2041  		t.Fatalf("len(world) != 4, = %d", len(world))
  2042  	}
  2043  
  2044  	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
  2045  		t.Fatalf("len(universe) != 2, = %d", len(universe))
  2046  	}
  2047  
  2048  	if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 {
  2049  		t.Fatalf("len(ints) != 2, = %d", len(ints))
  2050  	}
  2051  
  2052  	if fu := v.GetString("fu"); fu != "bar" {
  2053  		t.Fatalf("fu != \"bar\", = %s", fu)
  2054  	}
  2055  }
  2056  
  2057  func TestMergeConfigOverrideType(t *testing.T) {
  2058  	v := New()
  2059  	v.SetConfigType("json")
  2060  	if err := v.ReadConfig(bytes.NewBuffer(jsonMergeExampleTgt)); err != nil {
  2061  		t.Fatal(err)
  2062  	}
  2063  
  2064  	if err := v.MergeConfig(bytes.NewBuffer(jsonMergeExampleSrc)); err != nil {
  2065  		t.Fatal(err)
  2066  	}
  2067  
  2068  	if pop := v.GetString("hello.pop"); pop != "pop str" {
  2069  		t.Fatalf("pop != \"pop str\", = %s", pop)
  2070  	}
  2071  
  2072  	if foo := v.GetString("hello.foo"); foo != "foo str" {
  2073  		t.Fatalf("foo != \"foo str\", = %s", foo)
  2074  	}
  2075  }
  2076  
  2077  func TestMergeConfigNoMerge(t *testing.T) {
  2078  	v := New()
  2079  	v.SetConfigType("yml")
  2080  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  2081  		t.Fatal(err)
  2082  	}
  2083  
  2084  	if pop := v.GetInt("hello.pop"); pop != 37890 {
  2085  		t.Fatalf("pop != 37890, = %d", pop)
  2086  	}
  2087  
  2088  	if world := v.GetStringSlice("hello.world"); len(world) != 4 {
  2089  		t.Fatalf("len(world) != 4, = %d", len(world))
  2090  	}
  2091  
  2092  	if fu := v.GetString("fu"); fu != "" {
  2093  		t.Fatalf("fu != \"\", = %s", fu)
  2094  	}
  2095  
  2096  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleSrc)); err != nil {
  2097  		t.Fatal(err)
  2098  	}
  2099  
  2100  	if pop := v.GetInt("hello.pop"); pop != 45000 {
  2101  		t.Fatalf("pop != 45000, = %d", pop)
  2102  	}
  2103  
  2104  	if world := v.GetStringSlice("hello.world"); len(world) != 0 {
  2105  		t.Fatalf("len(world) != 0, = %d", len(world))
  2106  	}
  2107  
  2108  	if universe := v.GetStringSlice("hello.universe"); len(universe) != 2 {
  2109  		t.Fatalf("len(universe) != 2, = %d", len(universe))
  2110  	}
  2111  
  2112  	if ints := v.GetIntSlice("hello.ints"); len(ints) != 2 {
  2113  		t.Fatalf("len(ints) != 2, = %d", len(ints))
  2114  	}
  2115  
  2116  	if fu := v.GetString("fu"); fu != "bar" {
  2117  		t.Fatalf("fu != \"bar\", = %s", fu)
  2118  	}
  2119  }
  2120  
  2121  func TestMergeConfigMap(t *testing.T) {
  2122  	v := New()
  2123  	v.SetConfigType("yml")
  2124  	if err := v.ReadConfig(bytes.NewBuffer(yamlMergeExampleTgt)); err != nil {
  2125  		t.Fatal(err)
  2126  	}
  2127  
  2128  	assert := func(i int) {
  2129  		large := v.GetInt64("hello.largenum")
  2130  		pop := v.GetInt("hello.pop")
  2131  		if large != 765432101234567 {
  2132  			t.Fatal("Got large num:", large)
  2133  		}
  2134  
  2135  		if pop != i {
  2136  			t.Fatal("Got pop:", pop)
  2137  		}
  2138  	}
  2139  
  2140  	assert(37890)
  2141  
  2142  	update := map[string]interface{}{
  2143  		"Hello": map[string]interface{}{
  2144  			"Pop": 1234,
  2145  		},
  2146  		"World": map[interface{}]interface{}{
  2147  			"Rock": 345,
  2148  		},
  2149  	}
  2150  
  2151  	if err := v.MergeConfigMap(update); err != nil {
  2152  		t.Fatal(err)
  2153  	}
  2154  
  2155  	if rock := v.GetInt("world.rock"); rock != 345 {
  2156  		t.Fatal("Got rock:", rock)
  2157  	}
  2158  
  2159  	assert(1234)
  2160  }
  2161  
  2162  func TestUnmarshalingWithAliases(t *testing.T) {
  2163  	v := New()
  2164  	v.SetDefault("ID", 1)
  2165  	v.Set("name", "Steve")
  2166  	v.Set("lastname", "Owen")
  2167  
  2168  	v.RegisterAlias("UserID", "ID")
  2169  	v.RegisterAlias("Firstname", "name")
  2170  	v.RegisterAlias("Surname", "lastname")
  2171  
  2172  	type config struct {
  2173  		ID        int
  2174  		FirstName string
  2175  		Surname   string
  2176  	}
  2177  
  2178  	var C config
  2179  	err := v.Unmarshal(&C)
  2180  	if err != nil {
  2181  		t.Fatalf("unable to decode into struct, %v", err)
  2182  	}
  2183  
  2184  	assert.Equal(t, &config{ID: 1, FirstName: "Steve", Surname: "Owen"}, &C)
  2185  }
  2186  
  2187  func TestSetConfigNameClearsFileCache(t *testing.T) {
  2188  	SetConfigFile("/tmp/config.yaml")
  2189  	SetConfigName("default")
  2190  	f, err := v.getConfigFile()
  2191  	if err == nil {
  2192  		t.Fatalf("config file cache should have been cleared")
  2193  	}
  2194  	assert.Empty(t, f)
  2195  }
  2196  
  2197  func TestShadowedNestedValue(t *testing.T) {
  2198  	config := `name: steve
  2199  clothing:
  2200    jacket: leather
  2201    trousers: denim
  2202    pants:
  2203      size: large
  2204  `
  2205  	initConfig("yaml", config)
  2206  
  2207  	assert.Equal(t, "steve", GetString("name"))
  2208  
  2209  	polyester := "polyester"
  2210  	SetDefault("clothing.shirt", polyester)
  2211  	SetDefault("clothing.jacket.price", 100)
  2212  
  2213  	assert.Equal(t, "leather", GetString("clothing.jacket"))
  2214  	assert.Nil(t, Get("clothing.jacket.price"))
  2215  	assert.Equal(t, polyester, GetString("clothing.shirt"))
  2216  
  2217  	clothingSettings := AllSettings()["clothing"].(map[string]interface{})
  2218  	assert.Equal(t, "leather", clothingSettings["jacket"])
  2219  	assert.Equal(t, polyester, clothingSettings["shirt"])
  2220  }
  2221  
  2222  func TestDotParameter(t *testing.T) {
  2223  	initJSON()
  2224  	// shoud take precedence over batters defined in jsonExample
  2225  	r := bytes.NewReader([]byte(`{ "batters.batter": [ { "type": "Small" } ] }`))
  2226  	unmarshalReader(r, v.config)
  2227  
  2228  	actual := Get("batters.batter")
  2229  	expected := []interface{}{map[string]interface{}{"type": "Small"}}
  2230  	assert.Equal(t, expected, actual)
  2231  }
  2232  
  2233  func TestCaseInsensitive(t *testing.T) {
  2234  	for _, config := range []struct {
  2235  		typ     string
  2236  		content string
  2237  	}{
  2238  		{"yaml", `
  2239  aBcD: 1
  2240  eF:
  2241    gH: 2
  2242    iJk: 3
  2243    Lm:
  2244      nO: 4
  2245      P:
  2246        Q: 5
  2247        R: 6
  2248  `},
  2249  		{"json", `{
  2250    "aBcD": 1,
  2251    "eF": {
  2252      "iJk": 3,
  2253      "Lm": {
  2254        "P": {
  2255          "Q": 5,
  2256          "R": 6
  2257        },
  2258        "nO": 4
  2259      },
  2260      "gH": 2
  2261    }
  2262  }`},
  2263  		{"toml", `aBcD = 1
  2264  [eF]
  2265  gH = 2
  2266  iJk = 3
  2267  [eF.Lm]
  2268  nO = 4
  2269  [eF.Lm.P]
  2270  Q = 5
  2271  R = 6
  2272  `},
  2273  	} {
  2274  		doTestCaseInsensitive(t, config.typ, config.content)
  2275  	}
  2276  }
  2277  
  2278  func TestCaseInsensitiveSet(t *testing.T) {
  2279  	Reset()
  2280  	m1 := map[string]interface{}{
  2281  		"Foo": 32,
  2282  		"Bar": map[interface{}]interface{}{
  2283  			"ABc": "A",
  2284  			"cDE": "B",
  2285  		},
  2286  	}
  2287  
  2288  	m2 := map[string]interface{}{
  2289  		"Foo": 52,
  2290  		"Bar": map[interface{}]interface{}{
  2291  			"bCd": "A",
  2292  			"eFG": "B",
  2293  		},
  2294  	}
  2295  
  2296  	Set("Given1", m1)
  2297  	Set("Number1", 42)
  2298  
  2299  	SetDefault("Given2", m2)
  2300  	SetDefault("Number2", 52)
  2301  
  2302  	// Verify SetDefault
  2303  	if v := Get("number2"); v != 52 {
  2304  		t.Fatalf("Expected 52 got %q", v)
  2305  	}
  2306  
  2307  	if v := Get("given2.foo"); v != 52 {
  2308  		t.Fatalf("Expected 52 got %q", v)
  2309  	}
  2310  
  2311  	if v := Get("given2.bar.bcd"); v != "A" {
  2312  		t.Fatalf("Expected A got %q", v)
  2313  	}
  2314  
  2315  	if _, ok := m2["Foo"]; !ok {
  2316  		t.Fatal("Input map changed")
  2317  	}
  2318  
  2319  	// Verify Set
  2320  	if v := Get("number1"); v != 42 {
  2321  		t.Fatalf("Expected 42 got %q", v)
  2322  	}
  2323  
  2324  	if v := Get("given1.foo"); v != 32 {
  2325  		t.Fatalf("Expected 32 got %q", v)
  2326  	}
  2327  
  2328  	if v := Get("given1.bar.abc"); v != "A" {
  2329  		t.Fatalf("Expected A got %q", v)
  2330  	}
  2331  
  2332  	if _, ok := m1["Foo"]; !ok {
  2333  		t.Fatal("Input map changed")
  2334  	}
  2335  }
  2336  
  2337  func TestParseNested(t *testing.T) {
  2338  	type duration struct {
  2339  		Delay time.Duration
  2340  	}
  2341  
  2342  	type item struct {
  2343  		Name   string
  2344  		Delay  time.Duration
  2345  		Nested duration
  2346  	}
  2347  
  2348  	config := `[[parent]]
  2349  	delay="100ms"
  2350  	[parent.nested]
  2351  	delay="200ms"
  2352  `
  2353  	initConfig("toml", config)
  2354  
  2355  	var items []item
  2356  	err := v.UnmarshalKey("parent", &items)
  2357  	if err != nil {
  2358  		t.Fatalf("unable to decode into struct, %v", err)
  2359  	}
  2360  
  2361  	assert.Equal(t, 1, len(items))
  2362  	assert.Equal(t, 100*time.Millisecond, items[0].Delay)
  2363  	assert.Equal(t, 200*time.Millisecond, items[0].Nested.Delay)
  2364  }
  2365  
  2366  func doTestCaseInsensitive(t *testing.T, typ, config string) {
  2367  	initConfig(typ, config)
  2368  	Set("RfD", true)
  2369  	assert.Equal(t, true, Get("rfd"))
  2370  	assert.Equal(t, true, Get("rFD"))
  2371  	assert.Equal(t, 1, cast.ToInt(Get("abcd")))
  2372  	assert.Equal(t, 1, cast.ToInt(Get("Abcd")))
  2373  	assert.Equal(t, 2, cast.ToInt(Get("ef.gh")))
  2374  	assert.Equal(t, 3, cast.ToInt(Get("ef.ijk")))
  2375  	assert.Equal(t, 4, cast.ToInt(Get("ef.lm.no")))
  2376  	assert.Equal(t, 5, cast.ToInt(Get("ef.lm.p.q")))
  2377  }
  2378  
  2379  func newViperWithConfigFile(t *testing.T) (*Viper, string, func()) {
  2380  	watchDir, err := ioutil.TempDir("", "")
  2381  	require.Nil(t, err)
  2382  	configFile := path.Join(watchDir, "config.yaml")
  2383  	err = ioutil.WriteFile(configFile, []byte("foo: bar\n"), 0o640)
  2384  	require.Nil(t, err)
  2385  	cleanup := func() {
  2386  		os.RemoveAll(watchDir)
  2387  	}
  2388  	v := New()
  2389  	v.SetConfigFile(configFile)
  2390  	err = v.ReadInConfig()
  2391  	require.Nil(t, err)
  2392  	require.Equal(t, "bar", v.Get("foo"))
  2393  	return v, configFile, cleanup
  2394  }
  2395  
  2396  func newViperWithSymlinkedConfigFile(t *testing.T) (*Viper, string, string, func()) {
  2397  	watchDir, err := ioutil.TempDir("", "")
  2398  	require.Nil(t, err)
  2399  	dataDir1 := path.Join(watchDir, "data1")
  2400  	err = os.Mkdir(dataDir1, 0o777)
  2401  	require.Nil(t, err)
  2402  	realConfigFile := path.Join(dataDir1, "config.yaml")
  2403  	t.Logf("Real config file location: %s\n", realConfigFile)
  2404  	err = ioutil.WriteFile(realConfigFile, []byte("foo: bar\n"), 0o640)
  2405  	require.Nil(t, err)
  2406  	cleanup := func() {
  2407  		os.RemoveAll(watchDir)
  2408  	}
  2409  	// now, symlink the tm `data1` dir to `data` in the baseDir
  2410  	os.Symlink(dataDir1, path.Join(watchDir, "data"))
  2411  	// and link the `<watchdir>/datadir1/config.yaml` to `<watchdir>/config.yaml`
  2412  	configFile := path.Join(watchDir, "config.yaml")
  2413  	os.Symlink(path.Join(watchDir, "data", "config.yaml"), configFile)
  2414  	t.Logf("Config file location: %s\n", path.Join(watchDir, "config.yaml"))
  2415  	// init Viper
  2416  	v := New()
  2417  	v.SetConfigFile(configFile)
  2418  	err = v.ReadInConfig()
  2419  	require.Nil(t, err)
  2420  	require.Equal(t, "bar", v.Get("foo"))
  2421  	return v, watchDir, configFile, cleanup
  2422  }
  2423  
  2424  func TestWatchFile(t *testing.T) {
  2425  	if runtime.GOOS == "linux" {
  2426  		// TODO(bep) FIX ME
  2427  		t.Skip("Skip test on Linux ...")
  2428  	}
  2429  
  2430  	t.Run("file content changed", func(t *testing.T) {
  2431  		// given a `config.yaml` file being watched
  2432  		v, configFile, cleanup := newViperWithConfigFile(t)
  2433  		defer cleanup()
  2434  		_, err := os.Stat(configFile)
  2435  		require.NoError(t, err)
  2436  		t.Logf("test config file: %s\n", configFile)
  2437  		wg := sync.WaitGroup{}
  2438  		wg.Add(1)
  2439  		var wgDoneOnce sync.Once // OnConfigChange is called twice on Windows
  2440  		v.OnConfigChange(func(in fsnotify.Event) {
  2441  			t.Logf("config file changed")
  2442  			wgDoneOnce.Do(func() {
  2443  				wg.Done()
  2444  			})
  2445  		})
  2446  		v.WatchConfig()
  2447  		// when overwriting the file and waiting for the custom change notification handler to be triggered
  2448  		err = ioutil.WriteFile(configFile, []byte("foo: baz\n"), 0o640)
  2449  		wg.Wait()
  2450  		// then the config value should have changed
  2451  		require.Nil(t, err)
  2452  		assert.Equal(t, "baz", v.Get("foo"))
  2453  	})
  2454  
  2455  	t.Run("link to real file changed (à la Kubernetes)", func(t *testing.T) {
  2456  		// skip if not executed on Linux
  2457  		if runtime.GOOS != "linux" {
  2458  			t.Skipf("Skipping test as symlink replacements don't work on non-linux environment...")
  2459  		}
  2460  		v, watchDir, _, _ := newViperWithSymlinkedConfigFile(t)
  2461  		// defer cleanup()
  2462  		wg := sync.WaitGroup{}
  2463  		v.WatchConfig()
  2464  		v.OnConfigChange(func(in fsnotify.Event) {
  2465  			t.Logf("config file changed")
  2466  			wg.Done()
  2467  		})
  2468  		wg.Add(1)
  2469  		// when link to another `config.yaml` file
  2470  		dataDir2 := path.Join(watchDir, "data2")
  2471  		err := os.Mkdir(dataDir2, 0o777)
  2472  		require.Nil(t, err)
  2473  		configFile2 := path.Join(dataDir2, "config.yaml")
  2474  		err = ioutil.WriteFile(configFile2, []byte("foo: baz\n"), 0o640)
  2475  		require.Nil(t, err)
  2476  		// change the symlink using the `ln -sfn` command
  2477  		err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run()
  2478  		require.Nil(t, err)
  2479  		wg.Wait()
  2480  		// then
  2481  		require.Nil(t, err)
  2482  		assert.Equal(t, "baz", v.Get("foo"))
  2483  	})
  2484  }
  2485  
  2486  func TestUnmarshal_DotSeparatorBackwardCompatibility(t *testing.T) {
  2487  	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  2488  	flags.String("foo.bar", "cobra_flag", "")
  2489  
  2490  	v := New()
  2491  	assert.NoError(t, v.BindPFlags(flags))
  2492  
  2493  	config := &struct {
  2494  		Foo struct {
  2495  			Bar string
  2496  		}
  2497  	}{}
  2498  
  2499  	assert.NoError(t, v.Unmarshal(config))
  2500  	assert.Equal(t, "cobra_flag", config.Foo.Bar)
  2501  }
  2502  
  2503  // var yamlExampleWithDot = []byte(`Hacker: true
  2504  // name: steve
  2505  // hobbies:
  2506  //     - skateboarding
  2507  //     - snowboarding
  2508  //     - go
  2509  // clothing:
  2510  //     jacket: leather
  2511  //     trousers: denim
  2512  //     pants:
  2513  //         size: large
  2514  // age: 35
  2515  // eyes : brown
  2516  // beard: true
  2517  // emails:
  2518  //     steve@hacker.com:
  2519  //         created: 01/02/03
  2520  //         active: true
  2521  // `)
  2522  
  2523  func TestKeyDelimiter(t *testing.T) {
  2524  	v := NewWithOptions(KeyDelimiter("::"))
  2525  	v.SetConfigType("yaml")
  2526  	r := strings.NewReader(string(yamlExampleWithDot))
  2527  
  2528  	err := v.unmarshalReader(r, v.config)
  2529  	require.NoError(t, err)
  2530  
  2531  	values := map[string]interface{}{
  2532  		"image": map[string]interface{}{
  2533  			"repository": "someImage",
  2534  			"tag":        "1.0.0",
  2535  		},
  2536  		"ingress": map[string]interface{}{
  2537  			"annotations": map[string]interface{}{
  2538  				"traefik.frontend.rule.type":                 "PathPrefix",
  2539  				"traefik.ingress.kubernetes.io/ssl-redirect": "true",
  2540  			},
  2541  		},
  2542  	}
  2543  
  2544  	v.SetDefault("charts::values", values)
  2545  
  2546  	assert.Equal(t, "leather", v.GetString("clothing::jacket"))
  2547  	assert.Equal(t, "01/02/03", v.GetString("emails::steve@hacker.com::created"))
  2548  
  2549  	type config struct {
  2550  		Charts struct {
  2551  			Values map[string]interface{}
  2552  		}
  2553  	}
  2554  
  2555  	expected := config{
  2556  		Charts: struct {
  2557  			Values map[string]interface{}
  2558  		}{
  2559  			Values: values,
  2560  		},
  2561  	}
  2562  
  2563  	var actual config
  2564  
  2565  	assert.NoError(t, v.Unmarshal(&actual))
  2566  
  2567  	assert.Equal(t, expected, actual)
  2568  }
  2569  
  2570  var yamlDeepNestedSlices = []byte(`TV:
  2571  - title: "The Expanse"
  2572    title_i18n:
  2573      USA: "The Expanse"
  2574      Japan: "エクスパンス -巨獣めざめる-"
  2575    seasons:
  2576    - first_released: "December 14, 2015"
  2577      episodes:
  2578      - title: "Dulcinea"
  2579        air_date: "December 14, 2015"
  2580      - title: "The Big Empty"
  2581        air_date: "December 15, 2015"
  2582      - title: "Remember the Cant"
  2583        air_date: "December 22, 2015"
  2584    - first_released: "February 1, 2017"
  2585      episodes:
  2586      - title: "Safe"
  2587        air_date: "February 1, 2017"
  2588      - title: "Doors & Corners"
  2589        air_date: "February 1, 2017"
  2590      - title: "Static"
  2591        air_date: "February 8, 2017"
  2592    episodes:
  2593      - ["Dulcinea", "The Big Empty", "Remember the Cant"]
  2594      - ["Safe", "Doors & Corners", "Static"]
  2595  `)
  2596  
  2597  func TestSliceIndexAccess(t *testing.T) {
  2598  	v.SetConfigType("yaml")
  2599  	r := strings.NewReader(string(yamlDeepNestedSlices))
  2600  
  2601  	err := v.unmarshalReader(r, v.config)
  2602  	require.NoError(t, err)
  2603  
  2604  	assert.Equal(t, "The Expanse", v.GetString("tv.0.title"))
  2605  	assert.Equal(t, "February 1, 2017", v.GetString("tv.0.seasons.1.first_released"))
  2606  	assert.Equal(t, "Static", v.GetString("tv.0.seasons.1.episodes.2.title"))
  2607  	assert.Equal(t, "December 15, 2015", v.GetString("tv.0.seasons.0.episodes.1.air_date"))
  2608  
  2609  	// Test nested keys with capital letters
  2610  	assert.Equal(t, "The Expanse", v.GetString("tv.0.title_i18n.USA"))
  2611  	assert.Equal(t, "エクスパンス -巨獣めざめる-", v.GetString("tv.0.title_i18n.Japan"))
  2612  
  2613  	// Test for index out of bounds
  2614  	assert.Equal(t, "", v.GetString("tv.0.seasons.2.first_released"))
  2615  
  2616  	// Accessing multidimensional arrays
  2617  	assert.Equal(t, "Static", v.GetString("tv.0.episodes.1.2"))
  2618  }
  2619  
  2620  func BenchmarkGetBool(b *testing.B) {
  2621  	key := "BenchmarkGetBool"
  2622  	v = New()
  2623  	v.Set(key, true)
  2624  
  2625  	for i := 0; i < b.N; i++ {
  2626  		if !v.GetBool(key) {
  2627  			b.Fatal("GetBool returned false")
  2628  		}
  2629  	}
  2630  }
  2631  
  2632  func BenchmarkGet(b *testing.B) {
  2633  	key := "BenchmarkGet"
  2634  	v = New()
  2635  	v.Set(key, true)
  2636  
  2637  	for i := 0; i < b.N; i++ {
  2638  		if !v.Get(key).(bool) {
  2639  			b.Fatal("Get returned false")
  2640  		}
  2641  	}
  2642  }
  2643  
  2644  // BenchmarkGetBoolFromMap is the "perfect result" for the above.
  2645  func BenchmarkGetBoolFromMap(b *testing.B) {
  2646  	m := make(map[string]bool)
  2647  	key := "BenchmarkGetBool"
  2648  	m[key] = true
  2649  
  2650  	for i := 0; i < b.N; i++ {
  2651  		if !m[key] {
  2652  			b.Fatal("Map value was false")
  2653  		}
  2654  	}
  2655  }
  2656  
  2657  // Skip some tests on Windows that kept failing when Windows was added to the CI as a target.
  2658  func skipWindows(t *testing.T) {
  2659  	if runtime.GOOS == "windows" {
  2660  		t.Skip("Skip test on Windows")
  2661  	}
  2662  }