...

Source file src/github.com/BurntSushi/toml/encode_test.go

Documentation: github.com/BurntSushi/toml

     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 TestEncodeOmitEmpty(t *testing.T) {
   185  	type compareable struct {
   186  		Bool bool `toml:"bool,omitempty"`
   187  	}
   188  	type uncomparable struct {
   189  		Field []string `toml:"field,omitempty"`
   190  	}
   191  	type nestedUncomparable struct {
   192  		Field uncomparable `toml:"uncomparable,omitempty"`
   193  		Bool  bool         `toml:"bool,omitempty"`
   194  	}
   195  	type simple struct {
   196  		Bool                bool               `toml:"bool,omitempty"`
   197  		String              string             `toml:"string,omitempty"`
   198  		Array               [0]byte            `toml:"array,omitempty"`
   199  		Slice               []int              `toml:"slice,omitempty"`
   200  		Map                 map[string]string  `toml:"map,omitempty"`
   201  		Time                time.Time          `toml:"time,omitempty"`
   202  		Compareable1        compareable        `toml:"compareable1,omitempty"`
   203  		Compareable2        compareable        `toml:"compareable2,omitempty"`
   204  		Uncomparable1       uncomparable       `toml:"uncomparable1,omitempty"`
   205  		Uncomparable2       uncomparable       `toml:"uncomparable2,omitempty"`
   206  		NestedUncomparable1 nestedUncomparable `toml:"nesteduncomparable1,omitempty"`
   207  		NestedUncomparable2 nestedUncomparable `toml:"nesteduncomparable2,omitempty"`
   208  	}
   209  
   210  	var v simple
   211  	encodeExpected(t, "fields with omitempty are omitted when empty", v, "", nil)
   212  	v = simple{
   213  		Bool:                true,
   214  		String:              " ",
   215  		Slice:               []int{2, 3, 4},
   216  		Map:                 map[string]string{"foo": "bar"},
   217  		Time:                time.Date(1985, 6, 18, 15, 16, 17, 0, time.UTC),
   218  		Compareable2:        compareable{true},
   219  		Uncomparable2:       uncomparable{[]string{"XXX"}},
   220  		NestedUncomparable1: nestedUncomparable{uncomparable{[]string{"XXX"}}, false},
   221  		NestedUncomparable2: nestedUncomparable{uncomparable{}, true},
   222  	}
   223  	expected := `bool = true
   224  string = " "
   225  slice = [2, 3, 4]
   226  time = 1985-06-18T15:16:17Z
   227  
   228  [map]
   229    foo = "bar"
   230  
   231  [compareable2]
   232    bool = true
   233  
   234  [uncomparable2]
   235    field = ["XXX"]
   236  
   237  [nesteduncomparable1]
   238    [nesteduncomparable1.uncomparable]
   239      field = ["XXX"]
   240  
   241  [nesteduncomparable2]
   242    bool = true
   243  `
   244  	encodeExpected(t, "fields with omitempty are not omitted when non-empty",
   245  		v, expected, nil)
   246  }
   247  
   248  func TestEncodeOmitEmptyPointer(t *testing.T) {
   249  	type s struct {
   250  		String *string `toml:"string,omitempty"`
   251  	}
   252  
   253  	t.Run("nil pointers", func(t *testing.T) {
   254  		var v struct {
   255  			String *string            `toml:"string,omitempty"`
   256  			Slice  *[]string          `toml:"slice,omitempty"`
   257  			Map    *map[string]string `toml:"map,omitempty"`
   258  			Struct *s                 `toml:"struct,omitempty"`
   259  		}
   260  		encodeExpected(t, "", v, ``, nil)
   261  	})
   262  
   263  	t.Run("zero values", func(t *testing.T) {
   264  		str := ""
   265  		sl := []string{}
   266  		m := map[string]string{}
   267  
   268  		v := struct {
   269  			String *string            `toml:"string,omitempty"`
   270  			Slice  *[]string          `toml:"slice,omitempty"`
   271  			Map    *map[string]string `toml:"map,omitempty"`
   272  			Struct *s                 `toml:"struct,omitempty"`
   273  		}{&str, &sl, &m, &s{&str}}
   274  		want := `string = ""
   275  slice = []
   276  
   277  [map]
   278  
   279  [struct]
   280    string = ""
   281  `
   282  		encodeExpected(t, "", v, want, nil)
   283  	})
   284  
   285  	t.Run("with values", func(t *testing.T) {
   286  		str := "XXX"
   287  		sl := []string{"XXX"}
   288  		m := map[string]string{"XXX": "XXX"}
   289  
   290  		v := struct {
   291  			String *string            `toml:"string,omitempty"`
   292  			Slice  *[]string          `toml:"slice,omitempty"`
   293  			Map    *map[string]string `toml:"map,omitempty"`
   294  			Struct *s                 `toml:"struct,omitempty"`
   295  		}{&str, &sl, &m, &s{&str}}
   296  		want := `string = "XXX"
   297  slice = ["XXX"]
   298  
   299  [map]
   300    XXX = "XXX"
   301  
   302  [struct]
   303    string = "XXX"
   304  `
   305  		encodeExpected(t, "", v, want, nil)
   306  	})
   307  }
   308  
   309  func TestEncodeOmitZero(t *testing.T) {
   310  	type simple struct {
   311  		Number   int     `toml:"number,omitzero"`
   312  		Real     float64 `toml:"real,omitzero"`
   313  		Unsigned uint    `toml:"unsigned,omitzero"`
   314  	}
   315  
   316  	value := simple{0, 0.0, uint(0)}
   317  	expected := ""
   318  
   319  	encodeExpected(t, "simple with omitzero, all zero", value, expected, nil)
   320  
   321  	value.Number = 10
   322  	value.Real = 20
   323  	value.Unsigned = 5
   324  	expected = `number = 10
   325  real = 20.0
   326  unsigned = 5
   327  `
   328  	encodeExpected(t, "simple with omitzero, non-zero", value, expected, nil)
   329  }
   330  
   331  func TestEncodeOmitemptyEmptyName(t *testing.T) {
   332  	type simple struct {
   333  		S []int `toml:",omitempty"`
   334  	}
   335  	v := simple{[]int{1, 2, 3}}
   336  	expected := "S = [1, 2, 3]\n"
   337  	encodeExpected(t, "simple with omitempty, no name, non-empty field",
   338  		v, expected, nil)
   339  }
   340  
   341  func TestEncodeAnonymousStruct(t *testing.T) {
   342  	type Inner struct{ N int }
   343  	type inner struct{ B int }
   344  	type Embedded struct {
   345  		Inner1 Inner
   346  		Inner2 Inner
   347  	}
   348  	type Outer0 struct {
   349  		Inner
   350  		inner
   351  	}
   352  	type Outer1 struct {
   353  		Inner `toml:"inner"`
   354  		inner `toml:"innerb"`
   355  	}
   356  	type Outer3 struct {
   357  		Embedded
   358  	}
   359  
   360  	v0 := Outer0{Inner{3}, inner{4}}
   361  	expected := "N = 3\nB = 4\n"
   362  	encodeExpected(t, "embedded anonymous untagged struct", v0, expected, nil)
   363  
   364  	v1 := Outer1{Inner{3}, inner{4}}
   365  	expected = "[inner]\n  N = 3\n\n[innerb]\n  B = 4\n"
   366  	encodeExpected(t, "embedded anonymous tagged struct", v1, expected, nil)
   367  
   368  	v3 := Outer3{Embedded: Embedded{Inner{3}, Inner{4}}}
   369  	expected = "[Inner1]\n  N = 3\n\n[Inner2]\n  N = 4\n"
   370  	encodeExpected(t, "embedded anonymous multiple fields", v3, expected, nil)
   371  }
   372  
   373  func TestEncodeAnonymousStructPointerField(t *testing.T) {
   374  	type Inner struct{ N int }
   375  	type Outer0 struct{ *Inner }
   376  	type Outer1 struct {
   377  		*Inner `toml:"inner"`
   378  	}
   379  
   380  	v0 := Outer0{}
   381  	expected := ""
   382  	encodeExpected(t, "nil anonymous untagged struct pointer field", v0, expected, nil)
   383  
   384  	v0 = Outer0{&Inner{3}}
   385  	expected = "N = 3\n"
   386  	encodeExpected(t, "non-nil anonymous untagged struct pointer field", v0, expected, nil)
   387  
   388  	v1 := Outer1{}
   389  	expected = ""
   390  	encodeExpected(t, "nil anonymous tagged struct pointer field", v1, expected, nil)
   391  
   392  	v1 = Outer1{&Inner{3}}
   393  	expected = "[inner]\n  N = 3\n"
   394  	encodeExpected(t, "non-nil anonymous tagged struct pointer field", v1, expected, nil)
   395  }
   396  
   397  func TestEncodeNestedAnonymousStructs(t *testing.T) {
   398  	type A struct{ A string }
   399  	type B struct{ B string }
   400  	type C struct{ C string }
   401  	type BC struct {
   402  		B
   403  		C
   404  	}
   405  	type Outer struct {
   406  		A
   407  		BC
   408  	}
   409  
   410  	v := &Outer{
   411  		A: A{
   412  			A: "a",
   413  		},
   414  		BC: BC{
   415  			B: B{
   416  				B: "b",
   417  			},
   418  			C: C{
   419  				C: "c",
   420  			},
   421  		},
   422  	}
   423  
   424  	expected := "A = \"a\"\nB = \"b\"\nC = \"c\"\n"
   425  	encodeExpected(t, "nested anonymous untagged structs", v, expected, nil)
   426  }
   427  
   428  type InnerForNextTest struct{ N int }
   429  
   430  func (InnerForNextTest) F() {}
   431  func (InnerForNextTest) G() {}
   432  
   433  func TestEncodeAnonymousNoStructField(t *testing.T) {
   434  	type Inner interface{ F() }
   435  	type inner interface{ G() }
   436  	type IntS []int
   437  	type intS []int
   438  	type Outer0 struct {
   439  		Inner
   440  		inner
   441  		IntS
   442  		intS
   443  	}
   444  
   445  	v0 := Outer0{
   446  		Inner: InnerForNextTest{3},
   447  		inner: InnerForNextTest{4},
   448  		IntS:  []int{5, 6},
   449  		intS:  []int{7, 8},
   450  	}
   451  	expected := "IntS = [5, 6]\n\n[Inner]\n  N = 3\n"
   452  	encodeExpected(t, "non struct anonymous field", v0, expected, nil)
   453  }
   454  
   455  func TestEncodeIgnoredFields(t *testing.T) {
   456  	type simple struct {
   457  		Number int `toml:"-"`
   458  	}
   459  	value := simple{}
   460  	expected := ""
   461  	encodeExpected(t, "ignored field", value, expected, nil)
   462  }
   463  
   464  func TestEncodeNaN(t *testing.T) {
   465  	s1 := struct {
   466  		Nan float64 `toml:"nan"`
   467  		Inf float64 `toml:"inf"`
   468  	}{math.NaN(), math.Inf(1)}
   469  	s2 := struct {
   470  		Nan float32 `toml:"nan"`
   471  		Inf float32 `toml:"inf"`
   472  	}{float32(math.NaN()), float32(math.Inf(-1))}
   473  	encodeExpected(t, "", s1, "nan = nan\ninf = +inf\n", nil)
   474  	encodeExpected(t, "", s2, "nan = nan\ninf = -inf\n", nil)
   475  }
   476  
   477  func TestEncodePrimitive(t *testing.T) {
   478  	type MyStruct struct {
   479  		Data  Primitive
   480  		DataA int
   481  		DataB string
   482  	}
   483  
   484  	decodeAndEncode := func(toml string) string {
   485  		var s MyStruct
   486  		_, err := Decode(toml, &s)
   487  		if err != nil {
   488  			t.Fatal(err)
   489  		}
   490  
   491  		var buf bytes.Buffer
   492  		err = NewEncoder(&buf).Encode(s)
   493  		if err != nil {
   494  			t.Fatal(err)
   495  		}
   496  		return buf.String()
   497  	}
   498  
   499  	original := `DataA = 1
   500  DataB = "bbb"
   501  Data = ["Foo", "Bar"]
   502  `
   503  	reEncoded := decodeAndEncode(decodeAndEncode(original))
   504  
   505  	if reEncoded != original {
   506  		t.Errorf(
   507  			"re-encoded not the same as original\noriginal:   %q\nre-encoded: %q",
   508  			original, reEncoded)
   509  	}
   510  }
   511  
   512  func TestEncodeError(t *testing.T) {
   513  	tests := []struct {
   514  		in      interface{}
   515  		wantErr string
   516  	}{
   517  		{make(chan int), "unsupported type for key '': chan"},
   518  		{struct{ C complex128 }{0}, "unsupported type: complex128"},
   519  		{[]complex128{0}, "unsupported type: complex128"},
   520  	}
   521  
   522  	for _, tt := range tests {
   523  		t.Run("", func(t *testing.T) {
   524  			err := NewEncoder(os.Stderr).Encode(tt.in)
   525  			if err == nil {
   526  				t.Fatal("err is nil")
   527  			}
   528  			if !errorContains(err, tt.wantErr) {
   529  				t.Errorf("wrong error\nhave: %q\nwant: %q", err, tt.wantErr)
   530  			}
   531  		})
   532  	}
   533  }
   534  
   535  type (
   536  	sound struct{ S string }
   537  	food  struct{ F []string }
   538  	fun   func()
   539  	cplx  complex128
   540  	ints  []int
   541  
   542  	sound2 struct{ S string }
   543  	food2  struct{ F []string }
   544  	fun2   func()
   545  	cplx2  complex128
   546  	ints2  []int
   547  )
   548  
   549  // This is intentionally wrong (pointer receiver)
   550  func (s *sound) MarshalText() ([]byte, error) { return []byte(s.S), nil }
   551  func (f food) MarshalText() ([]byte, error)   { return []byte(strings.Join(f.F, ", ")), nil }
   552  func (f fun) MarshalText() ([]byte, error)    { return []byte("why would you do this?"), nil }
   553  func (c cplx) MarshalText() ([]byte, error) {
   554  	cplx := complex128(c)
   555  	return []byte(fmt.Sprintf("(%f+%fi)", real(cplx), imag(cplx))), nil
   556  }
   557  
   558  func intsValue(is []int) []byte {
   559  	var buf bytes.Buffer
   560  	buf.WriteByte('<')
   561  	for i, v := range is {
   562  		if i > 0 {
   563  			buf.WriteByte(',')
   564  		}
   565  		buf.WriteString(strconv.Itoa(v))
   566  	}
   567  	buf.WriteByte('>')
   568  	return buf.Bytes()
   569  }
   570  
   571  func (is *ints) MarshalText() ([]byte, error) {
   572  	if is == nil {
   573  		return []byte("[]"), nil
   574  	}
   575  	return intsValue(*is), nil
   576  }
   577  
   578  func (s *sound2) MarshalTOML() ([]byte, error) { return []byte("\"" + s.S + "\""), nil }
   579  func (f food2) MarshalTOML() ([]byte, error) {
   580  	return []byte("[\"" + strings.Join(f.F, "\", \"") + "\"]"), nil
   581  }
   582  func (f fun2) MarshalTOML() ([]byte, error) { return []byte("\"why would you do this?\""), nil }
   583  func (c cplx2) MarshalTOML() ([]byte, error) {
   584  	cplx := complex128(c)
   585  	return []byte(fmt.Sprintf("\"(%f+%fi)\"", real(cplx), imag(cplx))), nil
   586  }
   587  func (is *ints2) MarshalTOML() ([]byte, error) {
   588  	// MarshalTOML must quote by self
   589  	if is == nil {
   590  		return []byte(`"[]"`), nil
   591  	}
   592  	return []byte(fmt.Sprintf(`"%s"`, intsValue(*is))), nil
   593  }
   594  
   595  func TestEncodeTextMarshaler(t *testing.T) {
   596  	x := struct {
   597  		Name    string
   598  		Labels  map[string]string
   599  		Sound   sound
   600  		Sound2  *sound
   601  		Food    food
   602  		Food2   *food
   603  		Complex cplx
   604  		Fun     fun
   605  		Ints    ints
   606  		Ints2   *ints2
   607  	}{
   608  		Name:   "Goblok",
   609  		Sound:  sound{"miauw"},
   610  		Sound2: &sound{"miauw"},
   611  		Labels: map[string]string{
   612  			"type":  "cat",
   613  			"color": "black",
   614  		},
   615  		Food:    food{[]string{"chicken", "fish"}},
   616  		Food2:   &food{[]string{"chicken", "fish"}},
   617  		Complex: complex(42, 666),
   618  		Fun:     func() { panic("x") },
   619  		Ints:    ints{1, 2, 3, 4},
   620  		Ints2:   &ints2{1, 2, 3, 4},
   621  	}
   622  
   623  	var buf bytes.Buffer
   624  	if err := NewEncoder(&buf).Encode(&x); err != nil {
   625  		t.Fatal(err)
   626  	}
   627  
   628  	want := `Name = "Goblok"
   629  Sound = "miauw"
   630  Sound2 = "miauw"
   631  Food = "chicken, fish"
   632  Food2 = "chicken, fish"
   633  Complex = "(42.000000+666.000000i)"
   634  Fun = "why would you do this?"
   635  Ints = "<1,2,3,4>"
   636  Ints2 = "<1,2,3,4>"
   637  
   638  [Labels]
   639    color = "black"
   640    type = "cat"
   641  `
   642  
   643  	if buf.String() != want {
   644  		t.Error("\n" + buf.String())
   645  	}
   646  }
   647  
   648  func TestEncodeTOMLMarshaler(t *testing.T) {
   649  	x := struct {
   650  		Name    string
   651  		Labels  map[string]string
   652  		Sound   sound2
   653  		Sound2  *sound2
   654  		Food    food2
   655  		Food2   *food2
   656  		Complex cplx2
   657  		Fun     fun2
   658  	}{
   659  		Name:   "Goblok",
   660  		Sound:  sound2{"miauw"},
   661  		Sound2: &sound2{"miauw"},
   662  		Labels: map[string]string{
   663  			"type":  "cat",
   664  			"color": "black",
   665  		},
   666  		Food:    food2{[]string{"chicken", "fish"}},
   667  		Food2:   &food2{[]string{"chicken", "fish"}},
   668  		Complex: complex(42, 666),
   669  		Fun:     func() { panic("x") },
   670  	}
   671  
   672  	var buf bytes.Buffer
   673  	if err := NewEncoder(&buf).Encode(x); err != nil {
   674  		t.Fatal(err)
   675  	}
   676  
   677  	want := `Name = "Goblok"
   678  Sound2 = "miauw"
   679  Food = ["chicken", "fish"]
   680  Food2 = ["chicken", "fish"]
   681  Complex = "(42.000000+666.000000i)"
   682  Fun = "why would you do this?"
   683  
   684  [Labels]
   685    color = "black"
   686    type = "cat"
   687  
   688  [Sound]
   689    S = "miauw"
   690  `
   691  
   692  	if buf.String() != want {
   693  		t.Error("\n" + buf.String())
   694  	}
   695  }
   696  
   697  type (
   698  	retNil1 string
   699  	retNil2 string
   700  )
   701  
   702  func (r retNil1) MarshalText() ([]byte, error) { return nil, nil }
   703  func (r retNil2) MarshalTOML() ([]byte, error) { return nil, nil }
   704  
   705  func TestEncodeEmpty(t *testing.T) {
   706  	t.Run("text", func(t *testing.T) {
   707  		var (
   708  			s   struct{ Text retNil1 }
   709  			buf bytes.Buffer
   710  		)
   711  		err := NewEncoder(&buf).Encode(s)
   712  		if err == nil {
   713  			t.Fatalf("no error, but expected an error; output:\n%s", buf.String())
   714  		}
   715  		if buf.String() != "" {
   716  			t.Error("\n" + buf.String())
   717  		}
   718  	})
   719  
   720  	t.Run("toml", func(t *testing.T) {
   721  		var (
   722  			s   struct{ Text retNil2 }
   723  			buf bytes.Buffer
   724  		)
   725  		err := NewEncoder(&buf).Encode(s)
   726  		if err == nil {
   727  			t.Fatalf("no error, but expected an error; output:\n%s", buf.String())
   728  		}
   729  		if buf.String() != "" {
   730  			t.Error("\n" + buf.String())
   731  		}
   732  	})
   733  }
   734  
   735  // Would previously fail on 32bit architectures; can test with:
   736  //
   737  //	GOARCH=386         go test -c &&  ./toml.test
   738  //	GOARCH=arm GOARM=7 go test -c && qemu-arm ./toml.test
   739  func TestEncode32bit(t *testing.T) {
   740  	type Inner struct {
   741  		A, B, C string
   742  	}
   743  	type Outer struct{ Inner }
   744  
   745  	encodeExpected(t, "embedded anonymous untagged struct",
   746  		Outer{Inner{"a", "b", "c"}},
   747  		"A = \"a\"\nB = \"b\"\nC = \"c\"\n",
   748  		nil)
   749  }
   750  
   751  // Skip invalid types if it has toml:"-"
   752  //
   753  // https://github.com/BurntSushi/toml/issues/345
   754  func TestEncodeSkipInvalidType(t *testing.T) {
   755  	buf := new(bytes.Buffer)
   756  	err := NewEncoder(buf).Encode(struct {
   757  		Str  string                 `toml:"str"`
   758  		Arr  []func()               `toml:"-"`
   759  		Map  map[string]interface{} `toml:"-"`
   760  		Func func()                 `toml:"-"`
   761  	}{
   762  		Str:  "a",
   763  		Arr:  []func(){func() {}},
   764  		Map:  map[string]interface{}{"f": func() {}},
   765  		Func: func() {},
   766  	})
   767  	if err != nil {
   768  		t.Fatal(err)
   769  	}
   770  
   771  	have := buf.String()
   772  	want := "str = \"a\"\n"
   773  	if have != want {
   774  		t.Errorf("\nwant: %q\nhave: %q\n", want, have)
   775  	}
   776  }
   777  
   778  func TestEncodeDuration(t *testing.T) {
   779  	tests := []time.Duration{
   780  		0,
   781  		time.Second,
   782  		time.Minute,
   783  		time.Hour,
   784  		248*time.Hour + 45*time.Minute + 24*time.Second,
   785  		12345678 * time.Nanosecond,
   786  		12345678 * time.Second,
   787  		4*time.Second + 2*time.Nanosecond,
   788  	}
   789  
   790  	for _, tt := range tests {
   791  		encodeExpected(t, tt.String(),
   792  			struct{ Dur time.Duration }{Dur: tt},
   793  			fmt.Sprintf("Dur = %q", tt), nil)
   794  	}
   795  }
   796  
   797  type jsonT struct {
   798  	Num  json.Number
   799  	NumP *json.Number
   800  	Arr  []json.Number
   801  	ArrP []*json.Number
   802  	Tbl  map[string]json.Number
   803  	TblP map[string]*json.Number
   804  }
   805  
   806  var (
   807  	n2, n4, n6 = json.Number("2"), json.Number("4"), json.Number("6")
   808  	f2, f4, f6 = json.Number("2.2"), json.Number("4.4"), json.Number("6.6")
   809  )
   810  
   811  func TestEncodeJSONNumber(t *testing.T) {
   812  	tests := []struct {
   813  		in   jsonT
   814  		want string
   815  	}{
   816  		{jsonT{}, "Num = 0"},
   817  		{jsonT{
   818  			Num:  "1",
   819  			NumP: &n2,
   820  			Arr:  []json.Number{"3"},
   821  			ArrP: []*json.Number{&n4},
   822  			Tbl:  map[string]json.Number{"k1": "5"},
   823  			TblP: map[string]*json.Number{"k2": &n6}}, `
   824  				Num = 1
   825  				NumP = 2
   826  				Arr = [3]
   827  				ArrP = [4]
   828  
   829  				[Tbl]
   830  				  k1 = 5
   831  
   832  				[TblP]
   833  				  k2 = 6
   834  		`},
   835  		{jsonT{
   836  			Num:  "1.1",
   837  			NumP: &f2,
   838  			Arr:  []json.Number{"3.3"},
   839  			ArrP: []*json.Number{&f4},
   840  			Tbl:  map[string]json.Number{"k1": "5.5"},
   841  			TblP: map[string]*json.Number{"k2": &f6}}, `
   842  				Num = 1.1
   843  				NumP = 2.2
   844  				Arr = [3.3]
   845  				ArrP = [4.4]
   846  
   847  				[Tbl]
   848  				  k1 = 5.5
   849  
   850  				[TblP]
   851  				  k2 = 6.6
   852  		`},
   853  	}
   854  
   855  	for _, tt := range tests {
   856  		t.Run("", func(t *testing.T) {
   857  			var buf bytes.Buffer
   858  			err := NewEncoder(&buf).Encode(tt.in)
   859  			if err != nil {
   860  				t.Fatal(err)
   861  			}
   862  
   863  			have := strings.TrimSpace(buf.String())
   864  			want := strings.ReplaceAll(strings.TrimSpace(tt.want), "\t", "")
   865  			if have != want {
   866  				t.Errorf("\nwant:\n%s\nhave:\n%s\n", want, have)
   867  			}
   868  		})
   869  	}
   870  }
   871  
   872  func TestEncode(t *testing.T) {
   873  	type Embedded struct {
   874  		Int int `toml:"_int"`
   875  	}
   876  	type NonStruct int
   877  
   878  	date := time.Date(2014, 5, 11, 19, 30, 40, 0, time.UTC)
   879  	dateStr := "2014-05-11T19:30:40Z"
   880  
   881  	tests := map[string]struct {
   882  		input      interface{}
   883  		wantOutput string
   884  		wantError  error
   885  	}{
   886  		"bool field": {
   887  			input: struct {
   888  				BoolTrue  bool
   889  				BoolFalse bool
   890  			}{true, false},
   891  			wantOutput: "BoolTrue = true\nBoolFalse = false\n",
   892  		},
   893  		"int fields": {
   894  			input: struct {
   895  				Int   int
   896  				Int8  int8
   897  				Int16 int16
   898  				Int32 int32
   899  				Int64 int64
   900  			}{1, 2, 3, 4, 5},
   901  			wantOutput: "Int = 1\nInt8 = 2\nInt16 = 3\nInt32 = 4\nInt64 = 5\n",
   902  		},
   903  		"uint fields": {
   904  			input: struct {
   905  				Uint   uint
   906  				Uint8  uint8
   907  				Uint16 uint16
   908  				Uint32 uint32
   909  				Uint64 uint64
   910  			}{1, 2, 3, 4, 5},
   911  			wantOutput: "Uint = 1\nUint8 = 2\nUint16 = 3\nUint32 = 4" +
   912  				"\nUint64 = 5\n",
   913  		},
   914  		"float fields": {
   915  			input: struct {
   916  				Float32 float32
   917  				Float64 float64
   918  			}{1.5, 2.5},
   919  			wantOutput: "Float32 = 1.5\nFloat64 = 2.5\n",
   920  		},
   921  		"string field": {
   922  			input:      struct{ String string }{"foo"},
   923  			wantOutput: "String = \"foo\"\n",
   924  		},
   925  		"string field with \\n escape": {
   926  			input:      struct{ String string }{"foo\n"},
   927  			wantOutput: "String = \"foo\\n\"\n",
   928  		},
   929  		"string field and unexported field": {
   930  			input: struct {
   931  				String     string
   932  				unexported int
   933  			}{"foo", 0},
   934  			wantOutput: "String = \"foo\"\n",
   935  		},
   936  		"datetime field in UTC": {
   937  			input:      struct{ Date time.Time }{date},
   938  			wantOutput: fmt.Sprintf("Date = %s\n", dateStr),
   939  		},
   940  		"datetime field as primitive": {
   941  			// Using a map here to fail if isStructOrMap() returns true for
   942  			// time.Time.
   943  			input: map[string]interface{}{
   944  				"Date": date,
   945  				"Int":  1,
   946  			},
   947  			wantOutput: fmt.Sprintf("Date = %s\nInt = 1\n", dateStr),
   948  		},
   949  		"array fields": {
   950  			input: struct {
   951  				IntArray0 [0]int
   952  				IntArray3 [3]int
   953  			}{[0]int{}, [3]int{1, 2, 3}},
   954  			wantOutput: "IntArray0 = []\nIntArray3 = [1, 2, 3]\n",
   955  		},
   956  		"slice fields": {
   957  			input: struct{ IntSliceNil, IntSlice0, IntSlice3 []int }{
   958  				nil, []int{}, []int{1, 2, 3},
   959  			},
   960  			wantOutput: "IntSlice0 = []\nIntSlice3 = [1, 2, 3]\n",
   961  		},
   962  		"datetime slices": {
   963  			input: struct{ DatetimeSlice []time.Time }{
   964  				[]time.Time{date, date},
   965  			},
   966  			wantOutput: fmt.Sprintf("DatetimeSlice = [%s, %s]\n",
   967  				dateStr, dateStr),
   968  		},
   969  		"nested arrays and slices": {
   970  			input: struct {
   971  				SliceOfArrays         [][2]int
   972  				ArrayOfSlices         [2][]int
   973  				SliceOfArraysOfSlices [][2][]int
   974  				ArrayOfSlicesOfArrays [2][][2]int
   975  				SliceOfMixedArrays    [][2]interface{}
   976  				ArrayOfMixedSlices    [2][]interface{}
   977  			}{
   978  				[][2]int{{1, 2}, {3, 4}},
   979  				[2][]int{{1, 2}, {3, 4}},
   980  				[][2][]int{
   981  					{
   982  						{1, 2}, {3, 4},
   983  					},
   984  					{
   985  						{5, 6}, {7, 8},
   986  					},
   987  				},
   988  				[2][][2]int{
   989  					{
   990  						{1, 2}, {3, 4},
   991  					},
   992  					{
   993  						{5, 6}, {7, 8},
   994  					},
   995  				},
   996  				[][2]interface{}{
   997  					{1, 2}, {"a", "b"},
   998  				},
   999  				[2][]interface{}{
  1000  					{1, 2}, {"a", "b"},
  1001  				},
  1002  			},
  1003  			wantOutput: `SliceOfArrays = [[1, 2], [3, 4]]
  1004  ArrayOfSlices = [[1, 2], [3, 4]]
  1005  SliceOfArraysOfSlices = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
  1006  ArrayOfSlicesOfArrays = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
  1007  SliceOfMixedArrays = [[1, 2], ["a", "b"]]
  1008  ArrayOfMixedSlices = [[1, 2], ["a", "b"]]
  1009  `,
  1010  		},
  1011  		"empty slice": {
  1012  			input:      struct{ Empty []interface{} }{[]interface{}{}},
  1013  			wantOutput: "Empty = []\n",
  1014  		},
  1015  		"(error) slice with element type mismatch (string and integer)": {
  1016  			input:      struct{ Mixed []interface{} }{[]interface{}{1, "a"}},
  1017  			wantOutput: "Mixed = [1, \"a\"]\n",
  1018  		},
  1019  		"(error) slice with element type mismatch (integer and float)": {
  1020  			input:      struct{ Mixed []interface{} }{[]interface{}{1, 2.5}},
  1021  			wantOutput: "Mixed = [1, 2.5]\n",
  1022  		},
  1023  		"slice with elems of differing Go types, same TOML types": {
  1024  			input: struct {
  1025  				MixedInts   []interface{}
  1026  				MixedFloats []interface{}
  1027  			}{
  1028  				[]interface{}{
  1029  					int(1), int8(2), int16(3), int32(4), int64(5),
  1030  					uint(1), uint8(2), uint16(3), uint32(4), uint64(5),
  1031  				},
  1032  				[]interface{}{float32(1.5), float64(2.5)},
  1033  			},
  1034  			wantOutput: "MixedInts = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]\n" +
  1035  				"MixedFloats = [1.5, 2.5]\n",
  1036  		},
  1037  		"(error) slice w/ element type mismatch (one is nested array)": {
  1038  			input: struct{ Mixed []interface{} }{
  1039  				[]interface{}{1, []interface{}{2}},
  1040  			},
  1041  			wantOutput: "Mixed = [1, [2]]\n",
  1042  		},
  1043  		"(error) slice with 1 nil element": {
  1044  			input:     struct{ NilElement1 []interface{} }{[]interface{}{nil}},
  1045  			wantError: errArrayNilElement,
  1046  		},
  1047  		"(error) slice with 1 nil element (and other non-nil elements)": {
  1048  			input: struct{ NilElement []interface{} }{
  1049  				[]interface{}{1, nil},
  1050  			},
  1051  			wantError: errArrayNilElement,
  1052  		},
  1053  		"simple map": {
  1054  			input:      map[string]int{"a": 1, "b": 2},
  1055  			wantOutput: "a = 1\nb = 2\n",
  1056  		},
  1057  		"map with interface{} value type": {
  1058  			input:      map[string]interface{}{"a": 1, "b": "c"},
  1059  			wantOutput: "a = 1\nb = \"c\"\n",
  1060  		},
  1061  		"map with interface{} value type, some of which are structs": {
  1062  			input: map[string]interface{}{
  1063  				"a": struct{ Int int }{2},
  1064  				"b": 1,
  1065  			},
  1066  			wantOutput: "b = 1\n\n[a]\n  Int = 2\n",
  1067  		},
  1068  		"nested map": {
  1069  			input: map[string]map[string]int{
  1070  				"a": {"b": 1},
  1071  				"c": {"d": 2},
  1072  			},
  1073  			wantOutput: "[a]\n  b = 1\n\n[c]\n  d = 2\n",
  1074  		},
  1075  		"nested struct": {
  1076  			input: struct{ Struct struct{ Int int } }{
  1077  				struct{ Int int }{1},
  1078  			},
  1079  			wantOutput: "[Struct]\n  Int = 1\n",
  1080  		},
  1081  		"nested struct and non-struct field": {
  1082  			input: struct {
  1083  				Struct struct{ Int int }
  1084  				Bool   bool
  1085  			}{struct{ Int int }{1}, true},
  1086  			wantOutput: "Bool = true\n\n[Struct]\n  Int = 1\n",
  1087  		},
  1088  		"2 nested structs": {
  1089  			input: struct{ Struct1, Struct2 struct{ Int int } }{
  1090  				struct{ Int int }{1}, struct{ Int int }{2},
  1091  			},
  1092  			wantOutput: "[Struct1]\n  Int = 1\n\n[Struct2]\n  Int = 2\n",
  1093  		},
  1094  		"deeply nested structs": {
  1095  			input: struct {
  1096  				Struct1, Struct2 struct{ Struct3 *struct{ Int int } }
  1097  			}{
  1098  				struct{ Struct3 *struct{ Int int } }{&struct{ Int int }{1}},
  1099  				struct{ Struct3 *struct{ Int int } }{nil},
  1100  			},
  1101  			wantOutput: "[Struct1]\n  [Struct1.Struct3]\n    Int = 1" +
  1102  				"\n\n[Struct2]\n",
  1103  		},
  1104  		"nested struct with nil struct elem": {
  1105  			input: struct {
  1106  				Struct struct{ Inner *struct{ Int int } }
  1107  			}{
  1108  				struct{ Inner *struct{ Int int } }{nil},
  1109  			},
  1110  			wantOutput: "[Struct]\n",
  1111  		},
  1112  		"nested struct with no fields": {
  1113  			input: struct {
  1114  				Struct struct{ Inner struct{} }
  1115  			}{
  1116  				struct{ Inner struct{} }{struct{}{}},
  1117  			},
  1118  			wantOutput: "[Struct]\n  [Struct.Inner]\n",
  1119  		},
  1120  		"struct with tags": {
  1121  			input: struct {
  1122  				Struct struct {
  1123  					Int int `toml:"_int"`
  1124  				} `toml:"_struct"`
  1125  				Bool bool `toml:"_bool"`
  1126  			}{
  1127  				struct {
  1128  					Int int `toml:"_int"`
  1129  				}{1}, true,
  1130  			},
  1131  			wantOutput: "_bool = true\n\n[_struct]\n  _int = 1\n",
  1132  		},
  1133  		"embedded struct": {
  1134  			input:      struct{ Embedded }{Embedded{1}},
  1135  			wantOutput: "_int = 1\n",
  1136  		},
  1137  		"embedded *struct": {
  1138  			input:      struct{ *Embedded }{&Embedded{1}},
  1139  			wantOutput: "_int = 1\n",
  1140  		},
  1141  		"nested embedded struct": {
  1142  			input: struct {
  1143  				Struct struct{ Embedded } `toml:"_struct"`
  1144  			}{struct{ Embedded }{Embedded{1}}},
  1145  			wantOutput: "[_struct]\n  _int = 1\n",
  1146  		},
  1147  		"nested embedded *struct": {
  1148  			input: struct {
  1149  				Struct struct{ *Embedded } `toml:"_struct"`
  1150  			}{struct{ *Embedded }{&Embedded{1}}},
  1151  			wantOutput: "[_struct]\n  _int = 1\n",
  1152  		},
  1153  		"embedded non-struct": {
  1154  			input:      struct{ NonStruct }{5},
  1155  			wantOutput: "NonStruct = 5\n",
  1156  		},
  1157  		"array of tables": {
  1158  			input: struct {
  1159  				Structs []*struct{ Int int } `toml:"struct"`
  1160  			}{
  1161  				[]*struct{ Int int }{{1}, {3}},
  1162  			},
  1163  			wantOutput: "[[struct]]\n  Int = 1\n\n[[struct]]\n  Int = 3\n",
  1164  		},
  1165  		"array of tables order": {
  1166  			input: map[string]interface{}{
  1167  				"map": map[string]interface{}{
  1168  					"zero": 5,
  1169  					"arr": []map[string]int{
  1170  						{
  1171  							"friend": 5,
  1172  						},
  1173  					},
  1174  				},
  1175  			},
  1176  			wantOutput: "[map]\n  zero = 5\n\n  [[map.arr]]\n    friend = 5\n",
  1177  		},
  1178  		"empty key name": {
  1179  			input:      map[string]int{"": 1},
  1180  			wantOutput: `"" = 1` + "\n",
  1181  		},
  1182  		"key with \\n escape": {
  1183  			input:      map[string]string{"\n": "\n"},
  1184  			wantOutput: `"\n" = "\n"` + "\n",
  1185  		},
  1186  
  1187  		"empty map name": {
  1188  			input: map[string]interface{}{
  1189  				"": map[string]int{"v": 1},
  1190  			},
  1191  			wantOutput: "[\"\"]\n  v = 1\n",
  1192  		},
  1193  		"(error) top-level slice": {
  1194  			input:     []struct{ Int int }{{1}, {2}, {3}},
  1195  			wantError: errNoKey,
  1196  		},
  1197  		"(error) map no string key": {
  1198  			input:     map[int]string{1: ""},
  1199  			wantError: errNonString,
  1200  		},
  1201  
  1202  		"tbl-in-arr-struct": {
  1203  			input: struct {
  1204  				Arr [][]struct{ A, B, C int }
  1205  			}{[][]struct{ A, B, C int }{{{1, 2, 3}, {4, 5, 6}}}},
  1206  			wantOutput: "Arr = [[{A = 1, B = 2, C = 3}, {A = 4, B = 5, C = 6}]]",
  1207  		},
  1208  
  1209  		"tbl-in-arr-map": {
  1210  			input: map[string]interface{}{
  1211  				"arr": []interface{}{[]interface{}{
  1212  					map[string]interface{}{
  1213  						"a": []interface{}{"hello", "world"},
  1214  						"b": []interface{}{1.12, 4.1},
  1215  						"c": 1,
  1216  						"d": map[string]interface{}{"e": "E"},
  1217  						"f": struct{ A, B int }{1, 2},
  1218  						"g": []struct{ A, B int }{{3, 4}, {5, 6}},
  1219  					},
  1220  				}},
  1221  			},
  1222  			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}]}]]`,
  1223  		},
  1224  
  1225  		"slice of slice": {
  1226  			input: struct {
  1227  				Slices [][]struct{ Int int }
  1228  			}{
  1229  				[][]struct{ Int int }{{{1}}, {{2}}, {{3}}},
  1230  			},
  1231  			wantOutput: "Slices = [[{Int = 1}], [{Int = 2}], [{Int = 3}]]",
  1232  		},
  1233  	}
  1234  	for label, test := range tests {
  1235  		encodeExpected(t, label, test.input, test.wantOutput, test.wantError)
  1236  	}
  1237  }
  1238  
  1239  func TestEncodeDoubleTags(t *testing.T) {
  1240  	// TODO: this needs fixing; it shouldn't emit two 'a =' keys.
  1241  	s := struct {
  1242  		A int `toml:"a"`
  1243  		B int `toml:"a"`
  1244  		C int `toml:"c"`
  1245  	}{1, 2, 3}
  1246  	buf := new(strings.Builder)
  1247  	err := NewEncoder(buf).Encode(s)
  1248  	if err != nil {
  1249  		t.Fatal(err)
  1250  	}
  1251  
  1252  	want := `a = 1
  1253  a = 2
  1254  c = 3
  1255  `
  1256  	if want != buf.String() {
  1257  		t.Errorf("\nhave: %s\nwant: %s\n", buf.String(), want)
  1258  	}
  1259  }
  1260  
  1261  type (
  1262  	Doc1 struct{ N string }
  1263  	Doc2 struct{ N string }
  1264  )
  1265  
  1266  func (d Doc1) MarshalTOML() ([]byte, error) { return []byte(`marshal_toml = "` + d.N + `"`), nil }
  1267  func (d Doc2) MarshalText() ([]byte, error) { return []byte(`marshal_text = "` + d.N + `"`), nil }
  1268  
  1269  // MarshalTOML and MarshalText on the top level type, rather than a field.
  1270  func TestMarshalDoc(t *testing.T) {
  1271  	t.Run("toml", func(t *testing.T) {
  1272  		var buf bytes.Buffer
  1273  		err := NewEncoder(&buf).Encode(Doc1{"asd"})
  1274  		if err != nil {
  1275  			t.Fatal(err)
  1276  		}
  1277  
  1278  		want := `marshal_toml = "asd"`
  1279  		if want != buf.String() {
  1280  			t.Errorf("\nhave: %s\nwant: %s\n", buf.String(), want)
  1281  		}
  1282  	})
  1283  
  1284  	t.Run("text", func(t *testing.T) {
  1285  		var buf bytes.Buffer
  1286  		err := NewEncoder(&buf).Encode(Doc2{"asd"})
  1287  		if err != nil {
  1288  			t.Fatal(err)
  1289  		}
  1290  
  1291  		want := `"marshal_text = \"asd\""`
  1292  		if want != buf.String() {
  1293  			t.Errorf("\nhave: %s\nwant: %s\n", buf.String(), want)
  1294  		}
  1295  	})
  1296  }
  1297  
  1298  func encodeExpected(t *testing.T, label string, val interface{}, want string, wantErr error) {
  1299  	t.Helper()
  1300  	t.Run(label, func(t *testing.T) {
  1301  		t.Helper()
  1302  		var buf bytes.Buffer
  1303  		err := NewEncoder(&buf).Encode(val)
  1304  		if err != wantErr {
  1305  			if wantErr != nil {
  1306  				if wantErr == errAnything && err != nil {
  1307  					return
  1308  				}
  1309  				t.Errorf("want Encode error %v, got %v", wantErr, err)
  1310  			} else {
  1311  				t.Errorf("Encode failed: %s", err)
  1312  			}
  1313  		}
  1314  		if err != nil {
  1315  			return
  1316  		}
  1317  
  1318  		have := strings.TrimSpace(buf.String())
  1319  		want = strings.TrimSpace(want)
  1320  		if want != have {
  1321  			t.Errorf("\nhave:\n%s\nwant:\n%s\n",
  1322  				"\t"+strings.ReplaceAll(have, "\n", "\n\t"),
  1323  				"\t"+strings.ReplaceAll(want, "\n", "\n\t"))
  1324  		}
  1325  	})
  1326  }
  1327  

View as plain text