git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/toml/encode_test.go (about)

     1  package toml
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"math"
     8  	"net"
     9  	"os"
    10  	"strconv"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  )
    15  
    16  func TestEncodeRoundTrip(t *testing.T) {
    17  	type Config struct {
    18  		Age        int
    19  		Cats       []string
    20  		Pi         float64
    21  		Perfection []int
    22  		DOB        time.Time
    23  		Ipaddress  net.IP
    24  	}
    25  
    26  	var inputs = Config{
    27  		Age:        13,
    28  		Cats:       []string{"one", "two", "three"},
    29  		Pi:         3.145,
    30  		Perfection: []int{11, 2, 3, 4},
    31  		DOB:        time.Now(),
    32  		Ipaddress:  net.ParseIP("192.168.59.254"),
    33  	}
    34  
    35  	var (
    36  		firstBuffer  bytes.Buffer
    37  		secondBuffer bytes.Buffer
    38  		outputs      Config
    39  	)
    40  	err := NewEncoder(&firstBuffer).Encode(inputs)
    41  	if err != nil {
    42  		t.Fatal(err)
    43  	}
    44  	_, err = Decode(firstBuffer.String(), &outputs)
    45  	if err != nil {
    46  		t.Logf("Could not decode:\n%s\n", firstBuffer.String())
    47  		t.Fatal(err)
    48  	}
    49  	err = NewEncoder(&secondBuffer).Encode(outputs)
    50  	if err != nil {
    51  		t.Fatal(err)
    52  	}
    53  	if firstBuffer.String() != secondBuffer.String() {
    54  		t.Errorf("%s\n\nIS NOT IDENTICAL TO\n\n%s", firstBuffer.String(), secondBuffer.String())
    55  	}
    56  }
    57  
    58  func TestEncodeNestedTableArrays(t *testing.T) {
    59  	type song struct {
    60  		Name string `toml:"name"`
    61  	}
    62  	type album struct {
    63  		Name  string `toml:"name"`
    64  		Songs []song `toml:"songs"`
    65  	}
    66  	type springsteen struct {
    67  		Albums []album `toml:"albums"`
    68  	}
    69  	value := springsteen{
    70  		[]album{
    71  			{"Born to Run",
    72  				[]song{{"Jungleland"}, {"Meeting Across the River"}}},
    73  			{"Born in the USA",
    74  				[]song{{"Glory Days"}, {"Dancing in the Dark"}}},
    75  		},
    76  	}
    77  	expected := `[[albums]]
    78    name = "Born to Run"
    79  
    80    [[albums.songs]]
    81      name = "Jungleland"
    82  
    83    [[albums.songs]]
    84      name = "Meeting Across the River"
    85  
    86  [[albums]]
    87    name = "Born in the USA"
    88  
    89    [[albums.songs]]
    90      name = "Glory Days"
    91  
    92    [[albums.songs]]
    93      name = "Dancing in the Dark"
    94  `
    95  	encodeExpected(t, "nested table arrays", value, expected, nil)
    96  }
    97  
    98  func TestEncodeArrayHashWithNormalHashOrder(t *testing.T) {
    99  	type Alpha struct {
   100  		V int
   101  	}
   102  	type Beta struct {
   103  		V int
   104  	}
   105  	type Conf struct {
   106  		V int
   107  		A Alpha
   108  		B []Beta
   109  	}
   110  
   111  	val := Conf{
   112  		V: 1,
   113  		A: Alpha{2},
   114  		B: []Beta{{3}},
   115  	}
   116  	expected := "V = 1\n\n[A]\n  V = 2\n\n[[B]]\n  V = 3\n"
   117  	encodeExpected(t, "array hash with normal hash order", val, expected, nil)
   118  }
   119  
   120  func TestEncodeOmitEmptyStruct(t *testing.T) {
   121  	type (
   122  		T     struct{ Int int }
   123  		Tpriv struct {
   124  			Int     int
   125  			private int
   126  		}
   127  		Ttime struct {
   128  			Time time.Time
   129  		}
   130  	)
   131  
   132  	tests := []struct {
   133  		in   interface{}
   134  		want string
   135  	}{
   136  		{struct {
   137  			F T `toml:"f,omitempty"`
   138  		}{}, ""},
   139  		{struct {
   140  			F T `toml:"f,omitempty"`
   141  		}{T{1}}, "[f]\n  Int = 1"},
   142  
   143  		{struct {
   144  			F Tpriv `toml:"f,omitempty"`
   145  		}{}, ""},
   146  		{struct {
   147  			F Tpriv `toml:"f,omitempty"`
   148  		}{Tpriv{1, 0}}, "[f]\n  Int = 1"},
   149  
   150  		// Private field being set also counts as "not empty".
   151  		{struct {
   152  			F Tpriv `toml:"f,omitempty"`
   153  		}{Tpriv{0, 1}}, "[f]\n  Int = 0"},
   154  
   155  		// time.Time is common use case, so test that explicitly.
   156  		{struct {
   157  			F Ttime `toml:"t,omitempty"`
   158  		}{}, ""},
   159  		{struct {
   160  			F Ttime `toml:"t,omitempty"`
   161  		}{Ttime{time.Time{}.Add(1)}}, "[t]\n  Time = 0001-01-01T00:00:00.000000001Z"},
   162  
   163  		// TODO: also test with MarshalText, MarshalTOML returning non-zero
   164  		// value.
   165  	}
   166  
   167  	for _, tt := range tests {
   168  		t.Run("", func(t *testing.T) {
   169  			buf := new(bytes.Buffer)
   170  
   171  			err := NewEncoder(buf).Encode(tt.in)
   172  			if err != nil {
   173  				t.Fatal(err)
   174  			}
   175  
   176  			have := strings.TrimSpace(buf.String())
   177  			if have != tt.want {
   178  				t.Errorf("\nhave:\n%s\nwant:\n%s", have, tt.want)
   179  			}
   180  		})
   181  	}
   182  }
   183  
   184  func TestEncodeWithOmitEmpty(t *testing.T) {
   185  	type uncomparable struct {
   186  		Field []string `toml:"Field,omitempty"`
   187  	}
   188  	type simple struct {
   189  		Bool          bool              `toml:"bool,omitempty"`
   190  		String        string            `toml:"string,omitempty"`
   191  		Array         [0]byte           `toml:"array,omitempty"`
   192  		Slice         []int             `toml:"slice,omitempty"`
   193  		Map           map[string]string `toml:"map,omitempty"`
   194  		Time          time.Time         `toml:"time,omitempty"`
   195  		Uncomparable1 uncomparable      `toml:"uncomparable1,omitempty"`
   196  		Uncomparable2 uncomparable      `toml:"uncomparable2,omitempty"`
   197  	}
   198  
   199  	var v simple
   200  	encodeExpected(t, "fields with omitempty are omitted when empty", v, `
   201  [uncomparable1]
   202  
   203  [uncomparable2]
   204  `, nil)
   205  	v = simple{
   206  		Bool:          true,
   207  		String:        " ",
   208  		Slice:         []int{2, 3, 4},
   209  		Map:           map[string]string{"foo": "bar"},
   210  		Time:          time.Date(1985, 6, 18, 15, 16, 17, 0, time.UTC),
   211  		Uncomparable2: uncomparable{[]string{"XXX"}},
   212  	}
   213  	expected := `bool = true
   214  string = " "
   215  slice = [2, 3, 4]
   216  time = 1985-06-18T15:16:17Z
   217  
   218  [map]
   219    foo = "bar"
   220  
   221  [uncomparable1]
   222  
   223  [uncomparable2]
   224    Field = ["XXX"]
   225  `
   226  	encodeExpected(t, "fields with omitempty are not omitted when non-empty",
   227  		v, expected, nil)
   228  }
   229  
   230  func TestEncodeWithOmitZero(t *testing.T) {
   231  	type simple struct {
   232  		Number   int     `toml:"number,omitzero"`
   233  		Real     float64 `toml:"real,omitzero"`
   234  		Unsigned uint    `toml:"unsigned,omitzero"`
   235  	}
   236  
   237  	value := simple{0, 0.0, uint(0)}
   238  	expected := ""
   239  
   240  	encodeExpected(t, "simple with omitzero, all zero", value, expected, nil)
   241  
   242  	value.Number = 10
   243  	value.Real = 20
   244  	value.Unsigned = 5
   245  	expected = `number = 10
   246  real = 20.0
   247  unsigned = 5
   248  `
   249  	encodeExpected(t, "simple with omitzero, non-zero", value, expected, nil)
   250  }
   251  
   252  func TestEncodeOmitemptyWithEmptyName(t *testing.T) {
   253  	type simple struct {
   254  		S []int `toml:",omitempty"`
   255  	}
   256  	v := simple{[]int{1, 2, 3}}
   257  	expected := "S = [1, 2, 3]\n"
   258  	encodeExpected(t, "simple with omitempty, no name, non-empty field",
   259  		v, expected, nil)
   260  }
   261  
   262  func TestEncodeAnonymousStruct(t *testing.T) {
   263  	type Inner struct{ N int }
   264  	type inner struct{ B int }
   265  	type Outer0 struct {
   266  		Inner
   267  		inner
   268  	}
   269  	type Outer1 struct {
   270  		Inner `toml:"inner"`
   271  		inner `toml:"innerb"`
   272  	}
   273  
   274  	v0 := Outer0{Inner{3}, inner{4}}
   275  	expected := "N = 3\nB = 4\n"
   276  	encodeExpected(t, "embedded anonymous untagged struct", v0, expected, nil)
   277  
   278  	v1 := Outer1{Inner{3}, inner{4}}
   279  	expected = "[inner]\n  N = 3\n\n[innerb]\n  B = 4\n"
   280  	encodeExpected(t, "embedded anonymous tagged struct", v1, expected, nil)
   281  }
   282  
   283  func TestEncodeAnonymousStructPointerField(t *testing.T) {
   284  	type Inner struct{ N int }
   285  	type Outer0 struct{ *Inner }
   286  	type Outer1 struct {
   287  		*Inner `toml:"inner"`
   288  	}
   289  
   290  	v0 := Outer0{}
   291  	expected := ""
   292  	encodeExpected(t, "nil anonymous untagged struct pointer field", v0, expected, nil)
   293  
   294  	v0 = Outer0{&Inner{3}}
   295  	expected = "N = 3\n"
   296  	encodeExpected(t, "non-nil anonymous untagged struct pointer field", v0, expected, nil)
   297  
   298  	v1 := Outer1{}
   299  	expected = ""
   300  	encodeExpected(t, "nil anonymous tagged struct pointer field", v1, expected, nil)
   301  
   302  	v1 = Outer1{&Inner{3}}
   303  	expected = "[inner]\n  N = 3\n"
   304  	encodeExpected(t, "non-nil anonymous tagged struct pointer field", v1, expected, nil)
   305  }
   306  
   307  func TestEncodeNestedAnonymousStructs(t *testing.T) {
   308  	type A struct{ A string }
   309  	type B struct{ B string }
   310  	type C struct{ C string }
   311  	type BC struct {
   312  		B
   313  		C
   314  	}
   315  	type Outer struct {
   316  		A
   317  		BC
   318  	}
   319  
   320  	v := &Outer{
   321  		A: A{
   322  			A: "a",
   323  		},
   324  		BC: BC{
   325  			B: B{
   326  				B: "b",
   327  			},
   328  			C: C{
   329  				C: "c",
   330  			},
   331  		},
   332  	}
   333  
   334  	expected := "A = \"a\"\nB = \"b\"\nC = \"c\"\n"
   335  	encodeExpected(t, "nested anonymous untagged structs", v, expected, nil)
   336  }
   337  
   338  type InnerForNextTest struct{ N int }
   339  
   340  func (InnerForNextTest) F() {}
   341  func (InnerForNextTest) G() {}
   342  
   343  func TestEncodeAnonymousNoStructField(t *testing.T) {
   344  	type Inner interface{ F() }
   345  	type inner interface{ G() }
   346  	type IntS []int
   347  	type intS []int
   348  	type Outer0 struct {
   349  		Inner
   350  		inner
   351  		IntS
   352  		intS
   353  	}
   354  
   355  	v0 := Outer0{
   356  		Inner: InnerForNextTest{3},
   357  		inner: InnerForNextTest{4},
   358  		IntS:  []int{5, 6},
   359  		intS:  []int{7, 8},
   360  	}
   361  	expected := "IntS = [5, 6]\n\n[Inner]\n  N = 3\n"
   362  	encodeExpected(t, "non struct anonymous field", v0, expected, nil)
   363  }
   364  
   365  func TestEncodeIgnoredFields(t *testing.T) {
   366  	type simple struct {
   367  		Number int `toml:"-"`
   368  	}
   369  	value := simple{}
   370  	expected := ""
   371  	encodeExpected(t, "ignored field", value, expected, nil)
   372  }
   373  
   374  func TestEncodeNaN(t *testing.T) {
   375  	s1 := struct {
   376  		Nan float64 `toml:"nan"`
   377  		Inf float64 `toml:"inf"`
   378  	}{math.NaN(), math.Inf(1)}
   379  	s2 := struct {
   380  		Nan float32 `toml:"nan"`
   381  		Inf float32 `toml:"inf"`
   382  	}{float32(math.NaN()), float32(math.Inf(-1))}
   383  	encodeExpected(t, "", s1, "nan = nan\ninf = +inf\n", nil)
   384  	encodeExpected(t, "", s2, "nan = nan\ninf = -inf\n", nil)
   385  }
   386  
   387  func TestEncodePrimitive(t *testing.T) {
   388  	type MyStruct struct {
   389  		Data  Primitive
   390  		DataA int
   391  		DataB string
   392  	}
   393  
   394  	decodeAndEncode := func(toml string) string {
   395  		var s MyStruct
   396  		_, err := Decode(toml, &s)
   397  		if err != nil {
   398  			t.Fatal(err)
   399  		}
   400  
   401  		var buf bytes.Buffer
   402  		err = NewEncoder(&buf).Encode(s)
   403  		if err != nil {
   404  			t.Fatal(err)
   405  		}
   406  		return buf.String()
   407  	}
   408  
   409  	original := `DataA = 1
   410  DataB = "bbb"
   411  Data = ["Foo", "Bar"]
   412  `
   413  	reEncoded := decodeAndEncode(decodeAndEncode(original))
   414  
   415  	if reEncoded != original {
   416  		t.Errorf(
   417  			"re-encoded not the same as original\noriginal:   %q\nre-encoded: %q",
   418  			original, reEncoded)
   419  	}
   420  }
   421  
   422  func TestEncodeError(t *testing.T) {
   423  	tests := []struct {
   424  		in      interface{}
   425  		wantErr string
   426  	}{
   427  		{make(chan int), "unsupported type for key '': chan"},
   428  		{struct{ C complex128 }{0}, "unsupported type: complex128"},
   429  		{[]complex128{0}, "unsupported type: complex128"},
   430  	}
   431  
   432  	for _, tt := range tests {
   433  		t.Run("", func(t *testing.T) {
   434  			err := NewEncoder(os.Stderr).Encode(tt.in)
   435  			if err == nil {
   436  				t.Fatal("err is nil")
   437  			}
   438  			if !errorContains(err, tt.wantErr) {
   439  				t.Errorf("wrong error\nhave: %q\nwant: %q", err, tt.wantErr)
   440  			}
   441  		})
   442  	}
   443  }
   444  
   445  type (
   446  	sound struct{ S string }
   447  	food  struct{ F []string }
   448  	fun   func()
   449  	cplx  complex128
   450  	ints  []int
   451  
   452  	sound2 struct{ S string }
   453  	food2  struct{ F []string }
   454  	fun2   func()
   455  	cplx2  complex128
   456  	ints2  []int
   457  )
   458  
   459  // This is intentionally wrong (pointer receiver)
   460  func (s *sound) MarshalText() ([]byte, error) { return []byte(s.S), nil }
   461  func (f food) MarshalText() ([]byte, error)   { return []byte(strings.Join(f.F, ", ")), nil }
   462  func (f fun) MarshalText() ([]byte, error)    { return []byte("why would you do this?"), nil }
   463  func (c cplx) MarshalText() ([]byte, error) {
   464  	cplx := complex128(c)
   465  	return []byte(fmt.Sprintf("(%f+%fi)", real(cplx), imag(cplx))), nil
   466  }
   467  
   468  func intsValue(is []int) []byte {
   469  	var buf bytes.Buffer
   470  	buf.WriteByte('<')
   471  	for i, v := range is {
   472  		if i > 0 {
   473  			buf.WriteByte(',')
   474  		}
   475  		buf.WriteString(strconv.Itoa(v))
   476  	}
   477  	buf.WriteByte('>')
   478  	return buf.Bytes()
   479  }
   480  
   481  func (is *ints) MarshalText() ([]byte, error) {
   482  	if is == nil {
   483  		return []byte("[]"), nil
   484  	}
   485  	return intsValue(*is), nil
   486  }
   487  
   488  func (s *sound2) MarshalTOML() ([]byte, error) { return []byte("\"" + s.S + "\""), nil }
   489  func (f food2) MarshalTOML() ([]byte, error) {
   490  	return []byte("[\"" + strings.Join(f.F, "\", \"") + "\"]"), nil
   491  }
   492  func (f fun2) MarshalTOML() ([]byte, error) { return []byte("\"why would you do this?\""), nil }
   493  func (c cplx2) MarshalTOML() ([]byte, error) {
   494  	cplx := complex128(c)
   495  	return []byte(fmt.Sprintf("\"(%f+%fi)\"", real(cplx), imag(cplx))), nil
   496  }
   497  func (is *ints2) MarshalTOML() ([]byte, error) {
   498  	// MarshalTOML must quote by self
   499  	if is == nil {
   500  		return []byte(`"[]"`), nil
   501  	}
   502  	return []byte(fmt.Sprintf(`"%s"`, intsValue(*is))), nil
   503  }
   504  
   505  func TestEncodeTextMarshaler(t *testing.T) {
   506  	x := struct {
   507  		Name    string
   508  		Labels  map[string]string
   509  		Sound   sound
   510  		Sound2  *sound
   511  		Food    food
   512  		Food2   *food
   513  		Complex cplx
   514  		Fun     fun
   515  		Ints    ints
   516  		Ints2   *ints2
   517  	}{
   518  		Name:   "Goblok",
   519  		Sound:  sound{"miauw"},
   520  		Sound2: &sound{"miauw"},
   521  		Labels: map[string]string{
   522  			"type":  "cat",
   523  			"color": "black",
   524  		},
   525  		Food:    food{[]string{"chicken", "fish"}},
   526  		Food2:   &food{[]string{"chicken", "fish"}},
   527  		Complex: complex(42, 666),
   528  		Fun:     func() { panic("x") },
   529  		Ints:    ints{1, 2, 3, 4},
   530  		Ints2:   &ints2{1, 2, 3, 4},
   531  	}
   532  
   533  	var buf bytes.Buffer
   534  	if err := NewEncoder(&buf).Encode(&x); err != nil {
   535  		t.Fatal(err)
   536  	}
   537  
   538  	want := `Name = "Goblok"
   539  Sound = "miauw"
   540  Sound2 = "miauw"
   541  Food = "chicken, fish"
   542  Food2 = "chicken, fish"
   543  Complex = "(42.000000+666.000000i)"
   544  Fun = "why would you do this?"
   545  Ints = "<1,2,3,4>"
   546  Ints2 = "<1,2,3,4>"
   547  
   548  [Labels]
   549    color = "black"
   550    type = "cat"
   551  `
   552  
   553  	if buf.String() != want {
   554  		t.Error("\n" + buf.String())
   555  	}
   556  }
   557  
   558  func TestEncodeTOMLMarshaler(t *testing.T) {
   559  	x := struct {
   560  		Name    string
   561  		Labels  map[string]string
   562  		Sound   sound2
   563  		Sound2  *sound2
   564  		Food    food2
   565  		Food2   *food2
   566  		Complex cplx2
   567  		Fun     fun2
   568  	}{
   569  		Name:   "Goblok",
   570  		Sound:  sound2{"miauw"},
   571  		Sound2: &sound2{"miauw"},
   572  		Labels: map[string]string{
   573  			"type":  "cat",
   574  			"color": "black",
   575  		},
   576  		Food:    food2{[]string{"chicken", "fish"}},
   577  		Food2:   &food2{[]string{"chicken", "fish"}},
   578  		Complex: complex(42, 666),
   579  		Fun:     func() { panic("x") },
   580  	}
   581  
   582  	var buf bytes.Buffer
   583  	if err := NewEncoder(&buf).Encode(x); err != nil {
   584  		t.Fatal(err)
   585  	}
   586  
   587  	want := `Name = "Goblok"
   588  Sound2 = "miauw"
   589  Food = ["chicken", "fish"]
   590  Food2 = ["chicken", "fish"]
   591  Complex = "(42.000000+666.000000i)"
   592  Fun = "why would you do this?"
   593  
   594  [Labels]
   595    color = "black"
   596    type = "cat"
   597  
   598  [Sound]
   599    S = "miauw"
   600  `
   601  
   602  	if buf.String() != want {
   603  		t.Error("\n" + buf.String())
   604  	}
   605  }
   606  
   607  type (
   608  	retNil1 string
   609  	retNil2 string
   610  )
   611  
   612  func (r retNil1) MarshalText() ([]byte, error) { return nil, nil }
   613  func (r retNil2) MarshalTOML() ([]byte, error) { return nil, nil }
   614  
   615  func TestEncodeEmpty(t *testing.T) {
   616  	t.Run("text", func(t *testing.T) {
   617  		var (
   618  			s   struct{ Text retNil1 }
   619  			buf bytes.Buffer
   620  		)
   621  		err := NewEncoder(&buf).Encode(s)
   622  		if err == nil {
   623  			t.Fatalf("no error, but expected an error; output:\n%s", buf.String())
   624  		}
   625  		if buf.String() != "" {
   626  			t.Error("\n" + buf.String())
   627  		}
   628  	})
   629  
   630  	t.Run("toml", func(t *testing.T) {
   631  		var (
   632  			s   struct{ Text retNil2 }
   633  			buf bytes.Buffer
   634  		)
   635  		err := NewEncoder(&buf).Encode(s)
   636  		if err == nil {
   637  			t.Fatalf("no error, but expected an error; output:\n%s", buf.String())
   638  		}
   639  		if buf.String() != "" {
   640  			t.Error("\n" + buf.String())
   641  		}
   642  	})
   643  }
   644  
   645  // Would previously fail on 32bit architectures; can test with:
   646  //
   647  //	GOARCH=386         go test -c &&  ./toml.test
   648  //	GOARCH=arm GOARM=7 go test -c && qemu-arm ./toml.test
   649  func TestEncode32bit(t *testing.T) {
   650  	type Inner struct {
   651  		A, B, C string
   652  	}
   653  	type Outer struct{ Inner }
   654  
   655  	encodeExpected(t, "embedded anonymous untagged struct",
   656  		Outer{Inner{"a", "b", "c"}},
   657  		"A = \"a\"\nB = \"b\"\nC = \"c\"\n",
   658  		nil)
   659  }
   660  
   661  // Skip invalid types if it has toml:"-"
   662  //
   663  // https://git.sr.ht/~pingoo/stdx/toml/issues/345
   664  func TestEncodeSkipInvalidType(t *testing.T) {
   665  	buf := new(bytes.Buffer)
   666  	err := NewEncoder(buf).Encode(struct {
   667  		Str  string                 `toml:"str"`
   668  		Arr  []func()               `toml:"-"`
   669  		Map  map[string]interface{} `toml:"-"`
   670  		Func func()                 `toml:"-"`
   671  	}{
   672  		Str:  "a",
   673  		Arr:  []func(){func() {}},
   674  		Map:  map[string]interface{}{"f": func() {}},
   675  		Func: func() {},
   676  	})
   677  	if err != nil {
   678  		t.Fatal(err)
   679  	}
   680  
   681  	have := buf.String()
   682  	want := "str = \"a\"\n"
   683  	if have != want {
   684  		t.Errorf("\nwant: %q\nhave: %q\n", want, have)
   685  	}
   686  }
   687  
   688  func TestEncodeDuration(t *testing.T) {
   689  	tests := []time.Duration{
   690  		0,
   691  		time.Second,
   692  		time.Minute,
   693  		time.Hour,
   694  		248*time.Hour + 45*time.Minute + 24*time.Second,
   695  		12345678 * time.Nanosecond,
   696  		12345678 * time.Second,
   697  		4*time.Second + 2*time.Nanosecond,
   698  	}
   699  
   700  	for _, tt := range tests {
   701  		encodeExpected(t, tt.String(),
   702  			struct{ Dur time.Duration }{Dur: tt},
   703  			fmt.Sprintf("Dur = %q", tt), nil)
   704  	}
   705  }
   706  
   707  type jsonT struct {
   708  	Num  json.Number
   709  	NumP *json.Number
   710  	Arr  []json.Number
   711  	ArrP []*json.Number
   712  	Tbl  map[string]json.Number
   713  	TblP map[string]*json.Number
   714  }
   715  
   716  var (
   717  	n2, n4, n6 = json.Number("2"), json.Number("4"), json.Number("6")
   718  	f2, f4, f6 = json.Number("2.2"), json.Number("4.4"), json.Number("6.6")
   719  )
   720  
   721  func TestEncodeJSONNumber(t *testing.T) {
   722  	tests := []struct {
   723  		in   jsonT
   724  		want string
   725  	}{
   726  		{jsonT{}, "Num = 0"},
   727  		{jsonT{
   728  			Num:  "1",
   729  			NumP: &n2,
   730  			Arr:  []json.Number{"3"},
   731  			ArrP: []*json.Number{&n4},
   732  			Tbl:  map[string]json.Number{"k1": "5"},
   733  			TblP: map[string]*json.Number{"k2": &n6}}, `
   734  				Num = 1
   735  				NumP = 2
   736  				Arr = [3]
   737  				ArrP = [4]
   738  
   739  				[Tbl]
   740  				  k1 = 5
   741  
   742  				[TblP]
   743  				  k2 = 6
   744  		`},
   745  		{jsonT{
   746  			Num:  "1.1",
   747  			NumP: &f2,
   748  			Arr:  []json.Number{"3.3"},
   749  			ArrP: []*json.Number{&f4},
   750  			Tbl:  map[string]json.Number{"k1": "5.5"},
   751  			TblP: map[string]*json.Number{"k2": &f6}}, `
   752  				Num = 1.1
   753  				NumP = 2.2
   754  				Arr = [3.3]
   755  				ArrP = [4.4]
   756  
   757  				[Tbl]
   758  				  k1 = 5.5
   759  
   760  				[TblP]
   761  				  k2 = 6.6
   762  		`},
   763  	}
   764  
   765  	for _, tt := range tests {
   766  		t.Run("", func(t *testing.T) {
   767  			var buf bytes.Buffer
   768  			err := NewEncoder(&buf).Encode(tt.in)
   769  			if err != nil {
   770  				t.Fatal(err)
   771  			}
   772  
   773  			have := strings.TrimSpace(buf.String())
   774  			want := strings.ReplaceAll(strings.TrimSpace(tt.want), "\t", "")
   775  			if have != want {
   776  				t.Errorf("\nwant:\n%s\nhave:\n%s\n", want, have)
   777  			}
   778  		})
   779  	}
   780  }
   781  
   782  func TestEncode(t *testing.T) {
   783  	type Embedded struct {
   784  		Int int `toml:"_int"`
   785  	}
   786  	type NonStruct int
   787  
   788  	date := time.Date(2014, 5, 11, 19, 30, 40, 0, time.UTC)
   789  	dateStr := "2014-05-11T19:30:40Z"
   790  
   791  	tests := map[string]struct {
   792  		input      interface{}
   793  		wantOutput string
   794  		wantError  error
   795  	}{
   796  		"bool field": {
   797  			input: struct {
   798  				BoolTrue  bool
   799  				BoolFalse bool
   800  			}{true, false},
   801  			wantOutput: "BoolTrue = true\nBoolFalse = false\n",
   802  		},
   803  		"int fields": {
   804  			input: struct {
   805  				Int   int
   806  				Int8  int8
   807  				Int16 int16
   808  				Int32 int32
   809  				Int64 int64
   810  			}{1, 2, 3, 4, 5},
   811  			wantOutput: "Int = 1\nInt8 = 2\nInt16 = 3\nInt32 = 4\nInt64 = 5\n",
   812  		},
   813  		"uint fields": {
   814  			input: struct {
   815  				Uint   uint
   816  				Uint8  uint8
   817  				Uint16 uint16
   818  				Uint32 uint32
   819  				Uint64 uint64
   820  			}{1, 2, 3, 4, 5},
   821  			wantOutput: "Uint = 1\nUint8 = 2\nUint16 = 3\nUint32 = 4" +
   822  				"\nUint64 = 5\n",
   823  		},
   824  		"float fields": {
   825  			input: struct {
   826  				Float32 float32
   827  				Float64 float64
   828  			}{1.5, 2.5},
   829  			wantOutput: "Float32 = 1.5\nFloat64 = 2.5\n",
   830  		},
   831  		"string field": {
   832  			input:      struct{ String string }{"foo"},
   833  			wantOutput: "String = \"foo\"\n",
   834  		},
   835  		"string field with \\n escape": {
   836  			input:      struct{ String string }{"foo\n"},
   837  			wantOutput: "String = \"foo\\n\"\n",
   838  		},
   839  		"string field and unexported field": {
   840  			input: struct {
   841  				String     string
   842  				unexported int
   843  			}{"foo", 0},
   844  			wantOutput: "String = \"foo\"\n",
   845  		},
   846  		"datetime field in UTC": {
   847  			input:      struct{ Date time.Time }{date},
   848  			wantOutput: fmt.Sprintf("Date = %s\n", dateStr),
   849  		},
   850  		"datetime field as primitive": {
   851  			// Using a map here to fail if isStructOrMap() returns true for
   852  			// time.Time.
   853  			input: map[string]interface{}{
   854  				"Date": date,
   855  				"Int":  1,
   856  			},
   857  			wantOutput: fmt.Sprintf("Date = %s\nInt = 1\n", dateStr),
   858  		},
   859  		"array fields": {
   860  			input: struct {
   861  				IntArray0 [0]int
   862  				IntArray3 [3]int
   863  			}{[0]int{}, [3]int{1, 2, 3}},
   864  			wantOutput: "IntArray0 = []\nIntArray3 = [1, 2, 3]\n",
   865  		},
   866  		"slice fields": {
   867  			input: struct{ IntSliceNil, IntSlice0, IntSlice3 []int }{
   868  				nil, []int{}, []int{1, 2, 3},
   869  			},
   870  			wantOutput: "IntSlice0 = []\nIntSlice3 = [1, 2, 3]\n",
   871  		},
   872  		"datetime slices": {
   873  			input: struct{ DatetimeSlice []time.Time }{
   874  				[]time.Time{date, date},
   875  			},
   876  			wantOutput: fmt.Sprintf("DatetimeSlice = [%s, %s]\n",
   877  				dateStr, dateStr),
   878  		},
   879  		"nested arrays and slices": {
   880  			input: struct {
   881  				SliceOfArrays         [][2]int
   882  				ArrayOfSlices         [2][]int
   883  				SliceOfArraysOfSlices [][2][]int
   884  				ArrayOfSlicesOfArrays [2][][2]int
   885  				SliceOfMixedArrays    [][2]interface{}
   886  				ArrayOfMixedSlices    [2][]interface{}
   887  			}{
   888  				[][2]int{{1, 2}, {3, 4}},
   889  				[2][]int{{1, 2}, {3, 4}},
   890  				[][2][]int{
   891  					{
   892  						{1, 2}, {3, 4},
   893  					},
   894  					{
   895  						{5, 6}, {7, 8},
   896  					},
   897  				},
   898  				[2][][2]int{
   899  					{
   900  						{1, 2}, {3, 4},
   901  					},
   902  					{
   903  						{5, 6}, {7, 8},
   904  					},
   905  				},
   906  				[][2]interface{}{
   907  					{1, 2}, {"a", "b"},
   908  				},
   909  				[2][]interface{}{
   910  					{1, 2}, {"a", "b"},
   911  				},
   912  			},
   913  			wantOutput: `SliceOfArrays = [[1, 2], [3, 4]]
   914  ArrayOfSlices = [[1, 2], [3, 4]]
   915  SliceOfArraysOfSlices = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
   916  ArrayOfSlicesOfArrays = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
   917  SliceOfMixedArrays = [[1, 2], ["a", "b"]]
   918  ArrayOfMixedSlices = [[1, 2], ["a", "b"]]
   919  `,
   920  		},
   921  		"empty slice": {
   922  			input:      struct{ Empty []interface{} }{[]interface{}{}},
   923  			wantOutput: "Empty = []\n",
   924  		},
   925  		"(error) slice with element type mismatch (string and integer)": {
   926  			input:      struct{ Mixed []interface{} }{[]interface{}{1, "a"}},
   927  			wantOutput: "Mixed = [1, \"a\"]\n",
   928  		},
   929  		"(error) slice with element type mismatch (integer and float)": {
   930  			input:      struct{ Mixed []interface{} }{[]interface{}{1, 2.5}},
   931  			wantOutput: "Mixed = [1, 2.5]\n",
   932  		},
   933  		"slice with elems of differing Go types, same TOML types": {
   934  			input: struct {
   935  				MixedInts   []interface{}
   936  				MixedFloats []interface{}
   937  			}{
   938  				[]interface{}{
   939  					int(1), int8(2), int16(3), int32(4), int64(5),
   940  					uint(1), uint8(2), uint16(3), uint32(4), uint64(5),
   941  				},
   942  				[]interface{}{float32(1.5), float64(2.5)},
   943  			},
   944  			wantOutput: "MixedInts = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]\n" +
   945  				"MixedFloats = [1.5, 2.5]\n",
   946  		},
   947  		"(error) slice w/ element type mismatch (one is nested array)": {
   948  			input: struct{ Mixed []interface{} }{
   949  				[]interface{}{1, []interface{}{2}},
   950  			},
   951  			wantOutput: "Mixed = [1, [2]]\n",
   952  		},
   953  		"(error) slice with 1 nil element": {
   954  			input:     struct{ NilElement1 []interface{} }{[]interface{}{nil}},
   955  			wantError: errArrayNilElement,
   956  		},
   957  		"(error) slice with 1 nil element (and other non-nil elements)": {
   958  			input: struct{ NilElement []interface{} }{
   959  				[]interface{}{1, nil},
   960  			},
   961  			wantError: errArrayNilElement,
   962  		},
   963  		"simple map": {
   964  			input:      map[string]int{"a": 1, "b": 2},
   965  			wantOutput: "a = 1\nb = 2\n",
   966  		},
   967  		"map with interface{} value type": {
   968  			input:      map[string]interface{}{"a": 1, "b": "c"},
   969  			wantOutput: "a = 1\nb = \"c\"\n",
   970  		},
   971  		"map with interface{} value type, some of which are structs": {
   972  			input: map[string]interface{}{
   973  				"a": struct{ Int int }{2},
   974  				"b": 1,
   975  			},
   976  			wantOutput: "b = 1\n\n[a]\n  Int = 2\n",
   977  		},
   978  		"nested map": {
   979  			input: map[string]map[string]int{
   980  				"a": {"b": 1},
   981  				"c": {"d": 2},
   982  			},
   983  			wantOutput: "[a]\n  b = 1\n\n[c]\n  d = 2\n",
   984  		},
   985  		"nested struct": {
   986  			input: struct{ Struct struct{ Int int } }{
   987  				struct{ Int int }{1},
   988  			},
   989  			wantOutput: "[Struct]\n  Int = 1\n",
   990  		},
   991  		"nested struct and non-struct field": {
   992  			input: struct {
   993  				Struct struct{ Int int }
   994  				Bool   bool
   995  			}{struct{ Int int }{1}, true},
   996  			wantOutput: "Bool = true\n\n[Struct]\n  Int = 1\n",
   997  		},
   998  		"2 nested structs": {
   999  			input: struct{ Struct1, Struct2 struct{ Int int } }{
  1000  				struct{ Int int }{1}, struct{ Int int }{2},
  1001  			},
  1002  			wantOutput: "[Struct1]\n  Int = 1\n\n[Struct2]\n  Int = 2\n",
  1003  		},
  1004  		"deeply nested structs": {
  1005  			input: struct {
  1006  				Struct1, Struct2 struct{ Struct3 *struct{ Int int } }
  1007  			}{
  1008  				struct{ Struct3 *struct{ Int int } }{&struct{ Int int }{1}},
  1009  				struct{ Struct3 *struct{ Int int } }{nil},
  1010  			},
  1011  			wantOutput: "[Struct1]\n  [Struct1.Struct3]\n    Int = 1" +
  1012  				"\n\n[Struct2]\n",
  1013  		},
  1014  		"nested struct with nil struct elem": {
  1015  			input: struct {
  1016  				Struct struct{ Inner *struct{ Int int } }
  1017  			}{
  1018  				struct{ Inner *struct{ Int int } }{nil},
  1019  			},
  1020  			wantOutput: "[Struct]\n",
  1021  		},
  1022  		"nested struct with no fields": {
  1023  			input: struct {
  1024  				Struct struct{ Inner struct{} }
  1025  			}{
  1026  				struct{ Inner struct{} }{struct{}{}},
  1027  			},
  1028  			wantOutput: "[Struct]\n  [Struct.Inner]\n",
  1029  		},
  1030  		"struct with tags": {
  1031  			input: struct {
  1032  				Struct struct {
  1033  					Int int `toml:"_int"`
  1034  				} `toml:"_struct"`
  1035  				Bool bool `toml:"_bool"`
  1036  			}{
  1037  				struct {
  1038  					Int int `toml:"_int"`
  1039  				}{1}, true,
  1040  			},
  1041  			wantOutput: "_bool = true\n\n[_struct]\n  _int = 1\n",
  1042  		},
  1043  		"embedded struct": {
  1044  			input:      struct{ Embedded }{Embedded{1}},
  1045  			wantOutput: "_int = 1\n",
  1046  		},
  1047  		"embedded *struct": {
  1048  			input:      struct{ *Embedded }{&Embedded{1}},
  1049  			wantOutput: "_int = 1\n",
  1050  		},
  1051  		"nested embedded struct": {
  1052  			input: struct {
  1053  				Struct struct{ Embedded } `toml:"_struct"`
  1054  			}{struct{ Embedded }{Embedded{1}}},
  1055  			wantOutput: "[_struct]\n  _int = 1\n",
  1056  		},
  1057  		"nested embedded *struct": {
  1058  			input: struct {
  1059  				Struct struct{ *Embedded } `toml:"_struct"`
  1060  			}{struct{ *Embedded }{&Embedded{1}}},
  1061  			wantOutput: "[_struct]\n  _int = 1\n",
  1062  		},
  1063  		"embedded non-struct": {
  1064  			input:      struct{ NonStruct }{5},
  1065  			wantOutput: "NonStruct = 5\n",
  1066  		},
  1067  		"array of tables": {
  1068  			input: struct {
  1069  				Structs []*struct{ Int int } `toml:"struct"`
  1070  			}{
  1071  				[]*struct{ Int int }{{1}, {3}},
  1072  			},
  1073  			wantOutput: "[[struct]]\n  Int = 1\n\n[[struct]]\n  Int = 3\n",
  1074  		},
  1075  		"array of tables order": {
  1076  			input: map[string]interface{}{
  1077  				"map": map[string]interface{}{
  1078  					"zero": 5,
  1079  					"arr": []map[string]int{
  1080  						{
  1081  							"friend": 5,
  1082  						},
  1083  					},
  1084  				},
  1085  			},
  1086  			wantOutput: "[map]\n  zero = 5\n\n  [[map.arr]]\n    friend = 5\n",
  1087  		},
  1088  		"empty key name": {
  1089  			input:      map[string]int{"": 1},
  1090  			wantOutput: `"" = 1` + "\n",
  1091  		},
  1092  		"key with \\n escape": {
  1093  			input:      map[string]string{"\n": "\n"},
  1094  			wantOutput: `"\n" = "\n"` + "\n",
  1095  		},
  1096  
  1097  		"empty map name": {
  1098  			input: map[string]interface{}{
  1099  				"": map[string]int{"v": 1},
  1100  			},
  1101  			wantOutput: "[\"\"]\n  v = 1\n",
  1102  		},
  1103  		"(error) top-level slice": {
  1104  			input:     []struct{ Int int }{{1}, {2}, {3}},
  1105  			wantError: errNoKey,
  1106  		},
  1107  		"(error) map no string key": {
  1108  			input:     map[int]string{1: ""},
  1109  			wantError: errNonString,
  1110  		},
  1111  
  1112  		"tbl-in-arr-struct": {
  1113  			input: struct {
  1114  				Arr [][]struct{ A, B, C int }
  1115  			}{[][]struct{ A, B, C int }{{{1, 2, 3}, {4, 5, 6}}}},
  1116  			wantOutput: "Arr = [[{A = 1, B = 2, C = 3}, {A = 4, B = 5, C = 6}]]",
  1117  		},
  1118  
  1119  		"tbl-in-arr-map": {
  1120  			input: map[string]interface{}{
  1121  				"arr": []interface{}{[]interface{}{
  1122  					map[string]interface{}{
  1123  						"a": []interface{}{"hello", "world"},
  1124  						"b": []interface{}{1.12, 4.1},
  1125  						"c": 1,
  1126  						"d": map[string]interface{}{"e": "E"},
  1127  						"f": struct{ A, B int }{1, 2},
  1128  						"g": []struct{ A, B int }{{3, 4}, {5, 6}},
  1129  					},
  1130  				}},
  1131  			},
  1132  			wantOutput: `arr = [[{a = ["hello", "world"], b = [1.12, 4.1], c = 1, d = {e = "E"}, f = {A = 1, B = 2}, g = [{A = 3, B = 4}, {A = 5, B = 6}]}]]`,
  1133  		},
  1134  
  1135  		"slice of slice": {
  1136  			input: struct {
  1137  				Slices [][]struct{ Int int }
  1138  			}{
  1139  				[][]struct{ Int int }{{{1}}, {{2}}, {{3}}},
  1140  			},
  1141  			wantOutput: "Slices = [[{Int = 1}], [{Int = 2}], [{Int = 3}]]",
  1142  		},
  1143  	}
  1144  	for label, test := range tests {
  1145  		encodeExpected(t, label, test.input, test.wantOutput, test.wantError)
  1146  	}
  1147  }
  1148  
  1149  func encodeExpected(t *testing.T, label string, val interface{}, want string, wantErr error) {
  1150  	t.Helper()
  1151  	t.Run(label, func(t *testing.T) {
  1152  		t.Helper()
  1153  		var buf bytes.Buffer
  1154  		err := NewEncoder(&buf).Encode(val)
  1155  		if err != wantErr {
  1156  			if wantErr != nil {
  1157  				if wantErr == errAnything && err != nil {
  1158  					return
  1159  				}
  1160  				t.Errorf("want Encode error %v, got %v", wantErr, err)
  1161  			} else {
  1162  				t.Errorf("Encode failed: %s", err)
  1163  			}
  1164  		}
  1165  		if err != nil {
  1166  			return
  1167  		}
  1168  
  1169  		have := strings.TrimSpace(buf.String())
  1170  		want = strings.TrimSpace(want)
  1171  		if want != have {
  1172  			t.Errorf("\nhave:\n%s\nwant:\n%s\n", have, want)
  1173  		}
  1174  	})
  1175  }