...

Source file src/sigs.k8s.io/yaml/goyaml.v2/encode_test.go

Documentation: sigs.k8s.io/yaml/goyaml.v2

     1  package yaml_test
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"math"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	"net"
    12  	"os"
    13  
    14  	. "gopkg.in/check.v1"
    15  	"sigs.k8s.io/yaml/goyaml.v2"
    16  )
    17  
    18  type jsonNumberT string
    19  
    20  func (j jsonNumberT) Int64() (int64, error) {
    21  	val, err := strconv.Atoi(string(j))
    22  	if err != nil {
    23  		return 0, err
    24  	}
    25  	return int64(val), nil
    26  }
    27  
    28  func (j jsonNumberT) Float64() (float64, error) {
    29  	return strconv.ParseFloat(string(j), 64)
    30  }
    31  
    32  func (j jsonNumberT) String() string {
    33  	return string(j)
    34  }
    35  
    36  var marshalIntTest = 123
    37  
    38  var marshalTests = []struct {
    39  	value interface{}
    40  	data  string
    41  }{
    42  	{
    43  		nil,
    44  		"null\n",
    45  	}, {
    46  		(*marshalerType)(nil),
    47  		"null\n",
    48  	}, {
    49  		&struct{}{},
    50  		"{}\n",
    51  	}, {
    52  		map[string]string{"v": "hi"},
    53  		"v: hi\n",
    54  	}, {
    55  		map[string]interface{}{"v": "hi"},
    56  		"v: hi\n",
    57  	}, {
    58  		map[string]string{"v": "true"},
    59  		"v: \"true\"\n",
    60  	}, {
    61  		map[string]string{"v": "false"},
    62  		"v: \"false\"\n",
    63  	}, {
    64  		map[string]interface{}{"v": true},
    65  		"v: true\n",
    66  	}, {
    67  		map[string]interface{}{"v": false},
    68  		"v: false\n",
    69  	}, {
    70  		map[string]interface{}{"v": 10},
    71  		"v: 10\n",
    72  	}, {
    73  		map[string]interface{}{"v": -10},
    74  		"v: -10\n",
    75  	}, {
    76  		map[string]uint{"v": 42},
    77  		"v: 42\n",
    78  	}, {
    79  		map[string]interface{}{"v": int64(4294967296)},
    80  		"v: 4294967296\n",
    81  	}, {
    82  		map[string]int64{"v": int64(4294967296)},
    83  		"v: 4294967296\n",
    84  	}, {
    85  		map[string]uint64{"v": 4294967296},
    86  		"v: 4294967296\n",
    87  	}, {
    88  		map[string]interface{}{"v": "10"},
    89  		"v: \"10\"\n",
    90  	}, {
    91  		map[string]interface{}{"v": 0.1},
    92  		"v: 0.1\n",
    93  	}, {
    94  		map[string]interface{}{"v": float64(0.1)},
    95  		"v: 0.1\n",
    96  	}, {
    97  		map[string]interface{}{"v": float32(0.99)},
    98  		"v: 0.99\n",
    99  	}, {
   100  		map[string]interface{}{"v": -0.1},
   101  		"v: -0.1\n",
   102  	}, {
   103  		map[string]interface{}{"v": math.Inf(+1)},
   104  		"v: .inf\n",
   105  	}, {
   106  		map[string]interface{}{"v": math.Inf(-1)},
   107  		"v: -.inf\n",
   108  	}, {
   109  		map[string]interface{}{"v": math.NaN()},
   110  		"v: .nan\n",
   111  	}, {
   112  		map[string]interface{}{"v": nil},
   113  		"v: null\n",
   114  	}, {
   115  		map[string]interface{}{"v": ""},
   116  		"v: \"\"\n",
   117  	}, {
   118  		map[string][]string{"v": []string{"A", "B"}},
   119  		"v:\n- A\n- B\n",
   120  	}, {
   121  		map[string][]string{"v": []string{"A", "B\nC"}},
   122  		"v:\n- A\n- |-\n  B\n  C\n",
   123  	}, {
   124  		map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}},
   125  		"v:\n- A\n- 1\n- B:\n  - 2\n  - 3\n",
   126  	}, {
   127  		map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}},
   128  		"a:\n  b: c\n",
   129  	}, {
   130  		map[string]interface{}{"a": "-"},
   131  		"a: '-'\n",
   132  	},
   133  
   134  	// Simple values.
   135  	{
   136  		&marshalIntTest,
   137  		"123\n",
   138  	},
   139  
   140  	// Structures
   141  	{
   142  		&struct{ Hello string }{"world"},
   143  		"hello: world\n",
   144  	}, {
   145  		&struct {
   146  			A struct {
   147  				B string
   148  			}
   149  		}{struct{ B string }{"c"}},
   150  		"a:\n  b: c\n",
   151  	}, {
   152  		&struct {
   153  			A *struct {
   154  				B string
   155  			}
   156  		}{&struct{ B string }{"c"}},
   157  		"a:\n  b: c\n",
   158  	}, {
   159  		&struct {
   160  			A *struct {
   161  				B string
   162  			}
   163  		}{},
   164  		"a: null\n",
   165  	}, {
   166  		&struct{ A int }{1},
   167  		"a: 1\n",
   168  	}, {
   169  		&struct{ A []int }{[]int{1, 2}},
   170  		"a:\n- 1\n- 2\n",
   171  	}, {
   172  		&struct{ A [2]int }{[2]int{1, 2}},
   173  		"a:\n- 1\n- 2\n",
   174  	}, {
   175  		&struct {
   176  			B int "a"
   177  		}{1},
   178  		"a: 1\n",
   179  	}, {
   180  		&struct{ A bool }{true},
   181  		"a: true\n",
   182  	},
   183  
   184  	// Conditional flag
   185  	{
   186  		&struct {
   187  			A int "a,omitempty"
   188  			B int "b,omitempty"
   189  		}{1, 0},
   190  		"a: 1\n",
   191  	}, {
   192  		&struct {
   193  			A int "a,omitempty"
   194  			B int "b,omitempty"
   195  		}{0, 0},
   196  		"{}\n",
   197  	}, {
   198  		&struct {
   199  			A *struct{ X, y int } "a,omitempty,flow"
   200  		}{&struct{ X, y int }{1, 2}},
   201  		"a: {x: 1}\n",
   202  	}, {
   203  		&struct {
   204  			A *struct{ X, y int } "a,omitempty,flow"
   205  		}{nil},
   206  		"{}\n",
   207  	}, {
   208  		&struct {
   209  			A *struct{ X, y int } "a,omitempty,flow"
   210  		}{&struct{ X, y int }{}},
   211  		"a: {x: 0}\n",
   212  	}, {
   213  		&struct {
   214  			A struct{ X, y int } "a,omitempty,flow"
   215  		}{struct{ X, y int }{1, 2}},
   216  		"a: {x: 1}\n",
   217  	}, {
   218  		&struct {
   219  			A struct{ X, y int } "a,omitempty,flow"
   220  		}{struct{ X, y int }{0, 1}},
   221  		"{}\n",
   222  	}, {
   223  		&struct {
   224  			A float64 "a,omitempty"
   225  			B float64 "b,omitempty"
   226  		}{1, 0},
   227  		"a: 1\n",
   228  	},
   229  	{
   230  		&struct {
   231  			T1 time.Time  "t1,omitempty"
   232  			T2 time.Time  "t2,omitempty"
   233  			T3 *time.Time "t3,omitempty"
   234  			T4 *time.Time "t4,omitempty"
   235  		}{
   236  			T2: time.Date(2018, 1, 9, 10, 40, 47, 0, time.UTC),
   237  			T4: newTime(time.Date(2098, 1, 9, 10, 40, 47, 0, time.UTC)),
   238  		},
   239  		"t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n",
   240  	},
   241  	// Nil interface that implements Marshaler.
   242  	{
   243  		map[string]yaml.Marshaler{
   244  			"a": nil,
   245  		},
   246  		"a: null\n",
   247  	},
   248  
   249  	// Flow flag
   250  	{
   251  		&struct {
   252  			A []int "a,flow"
   253  		}{[]int{1, 2}},
   254  		"a: [1, 2]\n",
   255  	}, {
   256  		&struct {
   257  			A map[string]string "a,flow"
   258  		}{map[string]string{"b": "c", "d": "e"}},
   259  		"a: {b: c, d: e}\n",
   260  	}, {
   261  		&struct {
   262  			A struct {
   263  				B, D string
   264  			} "a,flow"
   265  		}{struct{ B, D string }{"c", "e"}},
   266  		"a: {b: c, d: e}\n",
   267  	},
   268  
   269  	// Unexported field
   270  	{
   271  		&struct {
   272  			u int
   273  			A int
   274  		}{0, 1},
   275  		"a: 1\n",
   276  	},
   277  
   278  	// Ignored field
   279  	{
   280  		&struct {
   281  			A int
   282  			B int "-"
   283  		}{1, 2},
   284  		"a: 1\n",
   285  	},
   286  
   287  	// Struct inlining
   288  	{
   289  		&struct {
   290  			A int
   291  			C inlineB `yaml:",inline"`
   292  		}{1, inlineB{2, inlineC{3}}},
   293  		"a: 1\nb: 2\nc: 3\n",
   294  	},
   295  
   296  	// Map inlining
   297  	{
   298  		&struct {
   299  			A int
   300  			C map[string]int `yaml:",inline"`
   301  		}{1, map[string]int{"b": 2, "c": 3}},
   302  		"a: 1\nb: 2\nc: 3\n",
   303  	},
   304  
   305  	// Duration
   306  	{
   307  		map[string]time.Duration{"a": 3 * time.Second},
   308  		"a: 3s\n",
   309  	},
   310  
   311  	// Issue #24: bug in map merging logic.
   312  	{
   313  		map[string]string{"a": "<foo>"},
   314  		"a: <foo>\n",
   315  	},
   316  
   317  	// Issue #34: marshal unsupported base 60 floats quoted for compatibility
   318  	// with old YAML 1.1 parsers.
   319  	{
   320  		map[string]string{"a": "1:1"},
   321  		"a: \"1:1\"\n",
   322  	},
   323  
   324  	// Binary data.
   325  	{
   326  		map[string]string{"a": "\x00"},
   327  		"a: \"\\0\"\n",
   328  	}, {
   329  		map[string]string{"a": "\x80\x81\x82"},
   330  		"a: !!binary gIGC\n",
   331  	}, {
   332  		map[string]string{"a": strings.Repeat("\x90", 54)},
   333  		"a: !!binary |\n  " + strings.Repeat("kJCQ", 17) + "kJ\n  CQ\n",
   334  	},
   335  
   336  	// Ordered maps.
   337  	{
   338  		&yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}},
   339  		"b: 2\na: 1\nd: 4\nc: 3\nsub:\n  e: 5\n",
   340  	},
   341  
   342  	// Encode unicode as utf-8 rather than in escaped form.
   343  	{
   344  		map[string]string{"a": "你好"},
   345  		"a: 你好\n",
   346  	},
   347  
   348  	// Support encoding.TextMarshaler.
   349  	{
   350  		map[string]net.IP{"a": net.IPv4(1, 2, 3, 4)},
   351  		"a: 1.2.3.4\n",
   352  	},
   353  	// time.Time gets a timestamp tag.
   354  	{
   355  		map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC)},
   356  		"a: 2015-02-24T18:19:39Z\n",
   357  	},
   358  	{
   359  		map[string]*time.Time{"a": newTime(time.Date(2015, 2, 24, 18, 19, 39, 0, time.UTC))},
   360  		"a: 2015-02-24T18:19:39Z\n",
   361  	},
   362  	{
   363  		// This is confirmed to be properly decoded in Python (libyaml) without a timestamp tag.
   364  		map[string]time.Time{"a": time.Date(2015, 2, 24, 18, 19, 39, 123456789, time.FixedZone("FOO", -3*60*60))},
   365  		"a: 2015-02-24T18:19:39.123456789-03:00\n",
   366  	},
   367  	// Ensure timestamp-like strings are quoted.
   368  	{
   369  		map[string]string{"a": "2015-02-24T18:19:39Z"},
   370  		"a: \"2015-02-24T18:19:39Z\"\n",
   371  	},
   372  
   373  	// Ensure strings containing ": " are quoted (reported as PR #43, but not reproducible).
   374  	{
   375  		map[string]string{"a": "b: c"},
   376  		"a: 'b: c'\n",
   377  	},
   378  
   379  	// Containing hash mark ('#') in string should be quoted
   380  	{
   381  		map[string]string{"a": "Hello #comment"},
   382  		"a: 'Hello #comment'\n",
   383  	},
   384  	{
   385  		map[string]string{"a": "你好 #comment"},
   386  		"a: '你好 #comment'\n",
   387  	},
   388  	{
   389  		map[string]interface{}{"a": jsonNumberT("5")},
   390  		"a: 5\n",
   391  	},
   392  	{
   393  		map[string]interface{}{"a": jsonNumberT("100.5")},
   394  		"a: 100.5\n",
   395  	},
   396  	{
   397  		map[string]interface{}{"a": jsonNumberT("bogus")},
   398  		"a: bogus\n",
   399  	},
   400  }
   401  
   402  func (s *S) TestLineWrapping(c *C) {
   403  	var v = map[string]string{
   404  		"a": "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 ",
   405  	}
   406  	data, err := yaml.Marshal(v)
   407  	c.Assert(err, IsNil)
   408  	c.Assert(string(data), Equals,
   409  		"a: 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz\n" +
   410  		"  ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 '\n")
   411  
   412  	// The API does not allow this process to be reversed as it's intended
   413  	// for migration only. v3 drops this method and instead offers more
   414  	// control on a per encoding basis.
   415  	yaml.FutureLineWrap()
   416  
   417  	data, err = yaml.Marshal(v)
   418  	c.Assert(err, IsNil)
   419  	c.Assert(string(data), Equals,
   420  		"a: 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 '\n")
   421  }
   422  
   423  func (s *S) TestMarshal(c *C) {
   424  	defer os.Setenv("TZ", os.Getenv("TZ"))
   425  	os.Setenv("TZ", "UTC")
   426  	for i, item := range marshalTests {
   427  		c.Logf("test %d: %q", i, item.data)
   428  		data, err := yaml.Marshal(item.value)
   429  		c.Assert(err, IsNil)
   430  		c.Assert(string(data), Equals, item.data)
   431  	}
   432  }
   433  
   434  func (s *S) TestEncoderSingleDocument(c *C) {
   435  	for i, item := range marshalTests {
   436  		c.Logf("test %d. %q", i, item.data)
   437  		var buf bytes.Buffer
   438  		enc := yaml.NewEncoder(&buf)
   439  		err := enc.Encode(item.value)
   440  		c.Assert(err, Equals, nil)
   441  		err = enc.Close()
   442  		c.Assert(err, Equals, nil)
   443  		c.Assert(buf.String(), Equals, item.data)
   444  	}
   445  }
   446  
   447  func (s *S) TestEncoderMultipleDocuments(c *C) {
   448  	var buf bytes.Buffer
   449  	enc := yaml.NewEncoder(&buf)
   450  	err := enc.Encode(map[string]string{"a": "b"})
   451  	c.Assert(err, Equals, nil)
   452  	err = enc.Encode(map[string]string{"c": "d"})
   453  	c.Assert(err, Equals, nil)
   454  	err = enc.Close()
   455  	c.Assert(err, Equals, nil)
   456  	c.Assert(buf.String(), Equals, "a: b\n---\nc: d\n")
   457  }
   458  
   459  func (s *S) TestEncoderWriteError(c *C) {
   460  	enc := yaml.NewEncoder(errorWriter{})
   461  	err := enc.Encode(map[string]string{"a": "b"})
   462  	c.Assert(err, ErrorMatches, `yaml: write error: some write error`) // Data not flushed yet
   463  }
   464  
   465  type errorWriter struct{}
   466  
   467  func (errorWriter) Write([]byte) (int, error) {
   468  	return 0, fmt.Errorf("some write error")
   469  }
   470  
   471  var marshalErrorTests = []struct {
   472  	value interface{}
   473  	error string
   474  	panic string
   475  }{{
   476  	value: &struct {
   477  		B       int
   478  		inlineB ",inline"
   479  	}{1, inlineB{2, inlineC{3}}},
   480  	panic: `Duplicated key 'b' in struct struct \{ B int; .*`,
   481  }, {
   482  	value: &struct {
   483  		A int
   484  		B map[string]int ",inline"
   485  	}{1, map[string]int{"a": 2}},
   486  	panic: `Can't have key "a" in inlined map; conflicts with struct field`,
   487  }}
   488  
   489  func (s *S) TestMarshalErrors(c *C) {
   490  	for _, item := range marshalErrorTests {
   491  		if item.panic != "" {
   492  			c.Assert(func() { yaml.Marshal(item.value) }, PanicMatches, item.panic)
   493  		} else {
   494  			_, err := yaml.Marshal(item.value)
   495  			c.Assert(err, ErrorMatches, item.error)
   496  		}
   497  	}
   498  }
   499  
   500  func (s *S) TestMarshalTypeCache(c *C) {
   501  	var data []byte
   502  	var err error
   503  	func() {
   504  		type T struct{ A int }
   505  		data, err = yaml.Marshal(&T{})
   506  		c.Assert(err, IsNil)
   507  	}()
   508  	func() {
   509  		type T struct{ B int }
   510  		data, err = yaml.Marshal(&T{})
   511  		c.Assert(err, IsNil)
   512  	}()
   513  	c.Assert(string(data), Equals, "b: 0\n")
   514  }
   515  
   516  var marshalerTests = []struct {
   517  	data  string
   518  	value interface{}
   519  }{
   520  	{"_:\n  hi: there\n", map[interface{}]interface{}{"hi": "there"}},
   521  	{"_:\n- 1\n- A\n", []interface{}{1, "A"}},
   522  	{"_: 10\n", 10},
   523  	{"_: null\n", nil},
   524  	{"_: BAR!\n", "BAR!"},
   525  }
   526  
   527  type marshalerType struct {
   528  	value interface{}
   529  }
   530  
   531  func (o marshalerType) MarshalText() ([]byte, error) {
   532  	panic("MarshalText called on type with MarshalYAML")
   533  }
   534  
   535  func (o marshalerType) MarshalYAML() (interface{}, error) {
   536  	return o.value, nil
   537  }
   538  
   539  type marshalerValue struct {
   540  	Field marshalerType "_"
   541  }
   542  
   543  func (s *S) TestMarshaler(c *C) {
   544  	for _, item := range marshalerTests {
   545  		obj := &marshalerValue{}
   546  		obj.Field.value = item.value
   547  		data, err := yaml.Marshal(obj)
   548  		c.Assert(err, IsNil)
   549  		c.Assert(string(data), Equals, string(item.data))
   550  	}
   551  }
   552  
   553  func (s *S) TestMarshalerWholeDocument(c *C) {
   554  	obj := &marshalerType{}
   555  	obj.value = map[string]string{"hello": "world!"}
   556  	data, err := yaml.Marshal(obj)
   557  	c.Assert(err, IsNil)
   558  	c.Assert(string(data), Equals, "hello: world!\n")
   559  }
   560  
   561  type failingMarshaler struct{}
   562  
   563  func (ft *failingMarshaler) MarshalYAML() (interface{}, error) {
   564  	return nil, failingErr
   565  }
   566  
   567  func (s *S) TestMarshalerError(c *C) {
   568  	_, err := yaml.Marshal(&failingMarshaler{})
   569  	c.Assert(err, Equals, failingErr)
   570  }
   571  
   572  func (s *S) TestSortedOutput(c *C) {
   573  	order := []interface{}{
   574  		false,
   575  		true,
   576  		1,
   577  		uint(1),
   578  		1.0,
   579  		1.1,
   580  		1.2,
   581  		2,
   582  		uint(2),
   583  		2.0,
   584  		2.1,
   585  		"",
   586  		".1",
   587  		".2",
   588  		".a",
   589  		"1",
   590  		"2",
   591  		"a!10",
   592  		"a/0001",
   593  		"a/002",
   594  		"a/3",
   595  		"a/10",
   596  		"a/11",
   597  		"a/0012",
   598  		"a/100",
   599  		"a~10",
   600  		"ab/1",
   601  		"b/1",
   602  		"b/01",
   603  		"b/2",
   604  		"b/02",
   605  		"b/3",
   606  		"b/03",
   607  		"b1",
   608  		"b01",
   609  		"b3",
   610  		"c2.10",
   611  		"c10.2",
   612  		"d1",
   613  		"d7",
   614  		"d7abc",
   615  		"d12",
   616  		"d12a",
   617  	}
   618  	m := make(map[interface{}]int)
   619  	for _, k := range order {
   620  		m[k] = 1
   621  	}
   622  	data, err := yaml.Marshal(m)
   623  	c.Assert(err, IsNil)
   624  	out := "\n" + string(data)
   625  	last := 0
   626  	for i, k := range order {
   627  		repr := fmt.Sprint(k)
   628  		if s, ok := k.(string); ok {
   629  			if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil {
   630  				repr = `"` + repr + `"`
   631  			}
   632  		}
   633  		index := strings.Index(out, "\n"+repr+":")
   634  		if index == -1 {
   635  			c.Fatalf("%#v is not in the output: %#v", k, out)
   636  		}
   637  		if index < last {
   638  			c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out)
   639  		}
   640  		last = index
   641  	}
   642  }
   643  
   644  func newTime(t time.Time) *time.Time {
   645  	return &t
   646  }
   647  

View as plain text