...

Source file src/google.golang.org/api/internal/gensupport/json_test.go

Documentation: google.golang.org/api/internal/gensupport

     1  // Copyright 2015 Google LLC
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gensupport
     6  
     7  import (
     8  	"encoding/json"
     9  	"reflect"
    10  	"testing"
    11  
    12  	"google.golang.org/api/googleapi"
    13  )
    14  
    15  type CustomType struct {
    16  	Foo string `json:"foo,omitempty"`
    17  }
    18  
    19  type schema struct {
    20  	// Basic types
    21  	B    bool    `json:"b,omitempty"`
    22  	F    float64 `json:"f,omitempty"`
    23  	I    int64   `json:"i,omitempty"`
    24  	Istr int64   `json:"istr,omitempty,string"`
    25  	Str  string  `json:"str,omitempty"`
    26  
    27  	// Pointers to basic types
    28  	PB    *bool    `json:"pb,omitempty"`
    29  	PF    *float64 `json:"pf,omitempty"`
    30  	PI    *int64   `json:"pi,omitempty"`
    31  	PIStr *int64   `json:"pistr,omitempty,string"`
    32  	PStr  *string  `json:"pstr,omitempty"`
    33  
    34  	// Other types
    35  	Int64s          googleapi.Int64s         `json:"i64s,omitempty"`
    36  	S               []int                    `json:"s,omitempty"`
    37  	M               map[string]string        `json:"m,omitempty"`
    38  	Any             interface{}              `json:"any,omitempty"`
    39  	Child           *child                   `json:"child,omitempty"`
    40  	MapToAnyArray   map[string][]interface{} `json:"maptoanyarray,omitempty"`
    41  	MapToCustomType map[string]CustomType    `json:"maptocustomtype,omitempty"`
    42  
    43  	ForceSendFields []string `json:"-"`
    44  	NullFields      []string `json:"-"`
    45  }
    46  
    47  type child struct {
    48  	B bool `json:"childbool,omitempty"`
    49  }
    50  
    51  type testCase struct {
    52  	s    schema
    53  	want string
    54  }
    55  
    56  func TestBasics(t *testing.T) {
    57  	for _, tc := range []testCase{
    58  		{
    59  			s:    schema{},
    60  			want: `{}`,
    61  		},
    62  		{
    63  			s: schema{
    64  				ForceSendFields: []string{"B", "F", "I", "Istr", "Str", "PB", "PF", "PI", "PIStr", "PStr"},
    65  			},
    66  			want: `{"b":false,"f":0.0,"i":0,"istr":"0","str":""}`,
    67  		},
    68  		{
    69  			s: schema{
    70  				NullFields: []string{"B", "F", "I", "Istr", "Str", "PB", "PF", "PI", "PIStr", "PStr"},
    71  			},
    72  			want: `{"b":null,"f":null,"i":null,"istr":null,"str":null,"pb":null,"pf":null,"pi":null,"pistr":null,"pstr":null}`,
    73  		},
    74  		{
    75  			s: schema{
    76  				B:     true,
    77  				F:     1.2,
    78  				I:     1,
    79  				Istr:  2,
    80  				Str:   "a",
    81  				PB:    googleapi.Bool(true),
    82  				PF:    googleapi.Float64(1.2),
    83  				PI:    googleapi.Int64(int64(1)),
    84  				PIStr: googleapi.Int64(int64(2)),
    85  				PStr:  googleapi.String("a"),
    86  			},
    87  			want: `{"b":true,"f":1.2,"i":1,"istr":"2","str":"a","pb":true,"pf":1.2,"pi":1,"pistr":"2","pstr":"a"}`,
    88  		},
    89  		{
    90  			s: schema{
    91  				B:     false,
    92  				F:     0.0,
    93  				I:     0,
    94  				Istr:  0,
    95  				Str:   "",
    96  				PB:    googleapi.Bool(false),
    97  				PF:    googleapi.Float64(0.0),
    98  				PI:    googleapi.Int64(int64(0)),
    99  				PIStr: googleapi.Int64(int64(0)),
   100  				PStr:  googleapi.String(""),
   101  			},
   102  			want: `{"pb":false,"pf":0.0,"pi":0,"pistr":"0","pstr":""}`,
   103  		},
   104  		{
   105  			s: schema{
   106  				B:               false,
   107  				F:               0.0,
   108  				I:               0,
   109  				Istr:            0,
   110  				Str:             "",
   111  				PB:              googleapi.Bool(false),
   112  				PF:              googleapi.Float64(0.0),
   113  				PI:              googleapi.Int64(int64(0)),
   114  				PIStr:           googleapi.Int64(int64(0)),
   115  				PStr:            googleapi.String(""),
   116  				ForceSendFields: []string{"B", "F", "I", "Istr", "Str", "PB", "PF", "PI", "PIStr", "PStr"},
   117  			},
   118  			want: `{"b":false,"f":0.0,"i":0,"istr":"0","str":"","pb":false,"pf":0.0,"pi":0,"pistr":"0","pstr":""}`,
   119  		},
   120  		{
   121  			s: schema{
   122  				B:          false,
   123  				F:          0.0,
   124  				I:          0,
   125  				Istr:       0,
   126  				Str:        "",
   127  				PB:         googleapi.Bool(false),
   128  				PF:         googleapi.Float64(0.0),
   129  				PI:         googleapi.Int64(int64(0)),
   130  				PIStr:      googleapi.Int64(int64(0)),
   131  				PStr:       googleapi.String(""),
   132  				NullFields: []string{"B", "F", "I", "Istr", "Str"},
   133  			},
   134  			want: `{"b":null,"f":null,"i":null,"istr":null,"str":null,"pb":false,"pf":0.0,"pi":0,"pistr":"0","pstr":""}`,
   135  		},
   136  	} {
   137  		checkMarshalJSON(t, tc)
   138  	}
   139  }
   140  
   141  func TestSliceFields(t *testing.T) {
   142  	for _, tc := range []testCase{
   143  		{
   144  			s:    schema{},
   145  			want: `{}`,
   146  		},
   147  		{
   148  			s:    schema{S: []int{}, Int64s: googleapi.Int64s{}},
   149  			want: `{}`,
   150  		},
   151  		{
   152  			s:    schema{S: []int{1}, Int64s: googleapi.Int64s{1}},
   153  			want: `{"s":[1],"i64s":["1"]}`,
   154  		},
   155  		{
   156  			s: schema{
   157  				ForceSendFields: []string{"S", "Int64s"},
   158  			},
   159  			want: `{"s":[],"i64s":[]}`,
   160  		},
   161  		{
   162  			s: schema{
   163  				S:               []int{},
   164  				Int64s:          googleapi.Int64s{},
   165  				ForceSendFields: []string{"S", "Int64s"},
   166  			},
   167  			want: `{"s":[],"i64s":[]}`,
   168  		},
   169  		{
   170  			s: schema{
   171  				S:               []int{1},
   172  				Int64s:          googleapi.Int64s{1},
   173  				ForceSendFields: []string{"S", "Int64s"},
   174  			},
   175  			want: `{"s":[1],"i64s":["1"]}`,
   176  		},
   177  		{
   178  			s: schema{
   179  				NullFields: []string{"S", "Int64s"},
   180  			},
   181  			want: `{"s":null,"i64s":null}`,
   182  		},
   183  	} {
   184  		checkMarshalJSON(t, tc)
   185  	}
   186  }
   187  
   188  func TestMapField(t *testing.T) {
   189  	for _, tc := range []testCase{
   190  		{
   191  			s:    schema{},
   192  			want: `{}`,
   193  		},
   194  		{
   195  			s:    schema{M: make(map[string]string)},
   196  			want: `{}`,
   197  		},
   198  		{
   199  			s:    schema{M: map[string]string{"a": "b"}},
   200  			want: `{"m":{"a":"b"}}`,
   201  		},
   202  		{
   203  			s: schema{
   204  				ForceSendFields: []string{"M"},
   205  			},
   206  			want: `{"m":{}}`,
   207  		},
   208  		{
   209  			s: schema{
   210  				NullFields: []string{"M"},
   211  			},
   212  			want: `{"m":null}`,
   213  		},
   214  		{
   215  			s: schema{
   216  				M:               make(map[string]string),
   217  				ForceSendFields: []string{"M"},
   218  			},
   219  			want: `{"m":{}}`,
   220  		},
   221  		{
   222  			s: schema{
   223  				M:          make(map[string]string),
   224  				NullFields: []string{"M"},
   225  			},
   226  			want: `{"m":null}`,
   227  		},
   228  		{
   229  			s: schema{
   230  				M:               map[string]string{"a": "b"},
   231  				ForceSendFields: []string{"M"},
   232  			},
   233  			want: `{"m":{"a":"b"}}`,
   234  		},
   235  		{
   236  			s: schema{
   237  				M:          map[string]string{"a": "b"},
   238  				NullFields: []string{"M.a", "M."},
   239  			},
   240  			want: `{"m": {"a": null, "":null}}`,
   241  		},
   242  		{
   243  			s: schema{
   244  				M:          map[string]string{"a": "b"},
   245  				NullFields: []string{"M.c"},
   246  			},
   247  			want: `{"m": {"a": "b", "c": null}}`,
   248  		},
   249  		{
   250  			s: schema{
   251  				NullFields:      []string{"M.a"},
   252  				ForceSendFields: []string{"M"},
   253  			},
   254  			want: `{"m": {"a": null}}`,
   255  		},
   256  		{
   257  			s: schema{
   258  				NullFields: []string{"M.a"},
   259  			},
   260  			want: `{}`,
   261  		},
   262  		{
   263  			s: schema{
   264  				MapToCustomType: map[string]CustomType{
   265  					"a": {Foo: "foo"},
   266  				},
   267  				NullFields: []string{"MapToCustomType.b"},
   268  			},
   269  			want: `{"maptocustomtype": {"a": {"foo": "foo"}, "b": null}}`,
   270  		},
   271  	} {
   272  		checkMarshalJSON(t, tc)
   273  	}
   274  }
   275  
   276  func TestMapToAnyArray(t *testing.T) {
   277  	for _, tc := range []testCase{
   278  		{
   279  			s:    schema{},
   280  			want: `{}`,
   281  		},
   282  		{
   283  			s:    schema{MapToAnyArray: make(map[string][]interface{})},
   284  			want: `{}`,
   285  		},
   286  		{
   287  			s: schema{
   288  				MapToAnyArray: map[string][]interface{}{
   289  					"a": {2, "b"},
   290  				},
   291  			},
   292  			want: `{"maptoanyarray":{"a":[2, "b"]}}`,
   293  		},
   294  		{
   295  			s: schema{
   296  				MapToAnyArray: map[string][]interface{}{
   297  					"a": nil,
   298  				},
   299  			},
   300  			want: `{"maptoanyarray":{"a": null}}`,
   301  		},
   302  		{
   303  			s: schema{
   304  				MapToAnyArray: map[string][]interface{}{
   305  					"a": {nil},
   306  				},
   307  			},
   308  			want: `{"maptoanyarray":{"a":[null]}}`,
   309  		},
   310  		{
   311  			s: schema{
   312  				ForceSendFields: []string{"MapToAnyArray"},
   313  			},
   314  			want: `{"maptoanyarray":{}}`,
   315  		},
   316  		{
   317  			s: schema{
   318  				NullFields: []string{"MapToAnyArray"},
   319  			},
   320  			want: `{"maptoanyarray":null}`,
   321  		},
   322  		{
   323  			s: schema{
   324  				MapToAnyArray:   make(map[string][]interface{}),
   325  				ForceSendFields: []string{"MapToAnyArray"},
   326  			},
   327  			want: `{"maptoanyarray":{}}`,
   328  		},
   329  		{
   330  			s: schema{
   331  				MapToAnyArray: map[string][]interface{}{
   332  					"a": {2, "b"},
   333  				},
   334  				ForceSendFields: []string{"MapToAnyArray"},
   335  			},
   336  			want: `{"maptoanyarray":{"a":[2, "b"]}}`,
   337  		},
   338  	} {
   339  		checkMarshalJSON(t, tc)
   340  	}
   341  }
   342  
   343  type anyType struct {
   344  	Field int
   345  }
   346  
   347  func (a anyType) MarshalJSON() ([]byte, error) {
   348  	return []byte(`"anyType value"`), nil
   349  }
   350  
   351  func TestAnyField(t *testing.T) {
   352  	// ForceSendFields has no effect on nil interfaces and interfaces that contain nil pointers.
   353  	var nilAny *anyType
   354  	for _, tc := range []testCase{
   355  		{
   356  			s:    schema{},
   357  			want: `{}`,
   358  		},
   359  		{
   360  			s:    schema{Any: nilAny},
   361  			want: `{"any": null}`,
   362  		},
   363  		{
   364  			s:    schema{Any: &anyType{}},
   365  			want: `{"any":"anyType value"}`,
   366  		},
   367  		{
   368  			s:    schema{Any: anyType{}},
   369  			want: `{"any":"anyType value"}`,
   370  		},
   371  		{
   372  			s: schema{
   373  				ForceSendFields: []string{"Any"},
   374  			},
   375  			want: `{}`,
   376  		},
   377  		{
   378  			s: schema{
   379  				NullFields: []string{"Any"},
   380  			},
   381  			want: `{"any":null}`,
   382  		},
   383  		{
   384  			s: schema{
   385  				Any:             nilAny,
   386  				ForceSendFields: []string{"Any"},
   387  			},
   388  			want: `{"any": null}`,
   389  		},
   390  		{
   391  			s: schema{
   392  				Any:             &anyType{},
   393  				ForceSendFields: []string{"Any"},
   394  			},
   395  			want: `{"any":"anyType value"}`,
   396  		},
   397  		{
   398  			s: schema{
   399  				Any:             anyType{},
   400  				ForceSendFields: []string{"Any"},
   401  			},
   402  			want: `{"any":"anyType value"}`,
   403  		},
   404  	} {
   405  		checkMarshalJSON(t, tc)
   406  	}
   407  }
   408  
   409  func TestSubschema(t *testing.T) {
   410  	// Subschemas are always stored as pointers, so ForceSendFields has no effect on them.
   411  	for _, tc := range []testCase{
   412  		{
   413  			s:    schema{},
   414  			want: `{}`,
   415  		},
   416  		{
   417  			s: schema{
   418  				ForceSendFields: []string{"Child"},
   419  			},
   420  			want: `{}`,
   421  		},
   422  		{
   423  			s: schema{
   424  				NullFields: []string{"Child"},
   425  			},
   426  			want: `{"child":null}`,
   427  		},
   428  		{
   429  			s:    schema{Child: &child{}},
   430  			want: `{"child":{}}`,
   431  		},
   432  		{
   433  			s: schema{
   434  				Child:           &child{},
   435  				ForceSendFields: []string{"Child"},
   436  			},
   437  			want: `{"child":{}}`,
   438  		},
   439  		{
   440  			s:    schema{Child: &child{B: true}},
   441  			want: `{"child":{"childbool":true}}`,
   442  		},
   443  
   444  		{
   445  			s: schema{
   446  				Child:           &child{B: true},
   447  				ForceSendFields: []string{"Child"},
   448  			},
   449  			want: `{"child":{"childbool":true}}`,
   450  		},
   451  	} {
   452  		checkMarshalJSON(t, tc)
   453  	}
   454  }
   455  
   456  // checkMarshalJSON verifies that calling schemaToMap on tc.s yields a result which is equivalent to tc.want.
   457  func checkMarshalJSON(t *testing.T, tc testCase) {
   458  	doCheckMarshalJSON(t, tc.s, tc.s.ForceSendFields, tc.s.NullFields, tc.want)
   459  	if len(tc.s.ForceSendFields) == 0 && len(tc.s.NullFields) == 0 {
   460  		// verify that the code path used when ForceSendFields and NullFields
   461  		// are non-empty produces the same output as the fast path that is used
   462  		// when they are empty.
   463  		doCheckMarshalJSON(t, tc.s, []string{"dummy"}, []string{"dummy"}, tc.want)
   464  	}
   465  }
   466  
   467  func doCheckMarshalJSON(t *testing.T, s schema, forceSendFields, nullFields []string, wantJSON string) {
   468  	encoded, err := MarshalJSON(s, forceSendFields, nullFields)
   469  	if err != nil {
   470  		t.Fatalf("encoding json:\n got err: %v", err)
   471  	}
   472  
   473  	// The expected and obtained JSON can differ in field ordering, so unmarshal before comparing.
   474  	var got interface{}
   475  	var want interface{}
   476  	err = json.Unmarshal(encoded, &got)
   477  	if err != nil {
   478  		t.Fatalf("decoding json:\n got err: %v", err)
   479  	}
   480  	err = json.Unmarshal([]byte(wantJSON), &want)
   481  	if err != nil {
   482  		t.Fatalf("decoding json:\n got err: %v", err)
   483  	}
   484  	if !reflect.DeepEqual(got, want) {
   485  		t.Errorf("schemaToMap:\ngot :%v\nwant: %v", got, want)
   486  	}
   487  }
   488  
   489  func TestParseJSONTag(t *testing.T) {
   490  	for _, tc := range []struct {
   491  		tag  string
   492  		want jsonTag
   493  	}{
   494  		{
   495  			tag:  "-",
   496  			want: jsonTag{ignore: true},
   497  		}, {
   498  			tag:  "name,omitempty",
   499  			want: jsonTag{apiName: "name"},
   500  		}, {
   501  			tag:  "name,omitempty,string",
   502  			want: jsonTag{apiName: "name", stringFormat: true},
   503  		},
   504  	} {
   505  		got, err := parseJSONTag(tc.tag)
   506  		if err != nil {
   507  			t.Fatalf("parsing json:\n got err: %v\ntag: %q", err, tc.tag)
   508  		}
   509  		if !reflect.DeepEqual(got, tc.want) {
   510  			t.Errorf("parseJSONTage:\ngot :%v\nwant:%v", got, tc.want)
   511  		}
   512  	}
   513  }
   514  func TestParseMalformedJSONTag(t *testing.T) {
   515  	for _, tag := range []string{
   516  		"",
   517  		"name",
   518  		"name,",
   519  		"name,blah",
   520  		"name,blah,string",
   521  		",omitempty",
   522  		",omitempty,string",
   523  		"name,omitempty,string,blah",
   524  	} {
   525  		_, err := parseJSONTag(tag)
   526  		if err == nil {
   527  			t.Fatalf("parsing json: expected err, got nil for tag: %v", tag)
   528  		}
   529  	}
   530  }
   531  

View as plain text