...

Source file src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/roundtrip_test.go

Documentation: k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes

     1  /*
     2  Copyright 2024 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package modes_test
    18  
    19  import (
    20  	"fmt"
    21  	"math"
    22  	"reflect"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/fxamacker/cbor/v2"
    27  	"github.com/google/go-cmp/cmp"
    28  )
    29  
    30  func nilPointerFor[T interface{}]() *T {
    31  	return nil
    32  }
    33  
    34  // TestRoundtrip roundtrips object serialization to interface{} and back via CBOR.
    35  func TestRoundtrip(t *testing.T) {
    36  	type modePair struct {
    37  		enc cbor.EncMode
    38  		dec cbor.DecMode
    39  	}
    40  
    41  	for _, tc := range []struct {
    42  		name      string
    43  		modePairs []modePair
    44  		obj       interface{}
    45  	}{
    46  		{
    47  			name: "nil slice",
    48  			obj:  []interface{}(nil),
    49  		},
    50  		{
    51  			name: "nil map",
    52  			obj:  map[string]interface{}(nil),
    53  		},
    54  		{
    55  			name: "empty slice",
    56  			obj:  []interface{}{},
    57  		},
    58  		{
    59  			name: "empty map",
    60  			obj:  map[string]interface{}{},
    61  		},
    62  		{
    63  			name: "nil pointer to slice",
    64  			obj:  nilPointerFor[[]interface{}](),
    65  		},
    66  		{
    67  			name: "nil pointer to map",
    68  			obj:  nilPointerFor[map[string]interface{}](),
    69  		},
    70  		{
    71  			name: "nonempty string",
    72  			obj:  "hello world",
    73  		},
    74  		{
    75  			name: "empty string",
    76  			obj:  "",
    77  		},
    78  		{
    79  			name: "string containing invalid UTF-8 sequence",
    80  			obj:  "\x80", // first byte is a continuation byte
    81  		},
    82  		{
    83  			name: "true",
    84  			obj:  true,
    85  		},
    86  		{
    87  			name: "false",
    88  			obj:  false,
    89  		},
    90  		{
    91  			name: "int64",
    92  			obj:  int64(5),
    93  		},
    94  		{
    95  			name: "int64 max",
    96  			obj:  int64(math.MaxInt64),
    97  		},
    98  		{
    99  			name: "int64 min",
   100  			obj:  int64(math.MinInt64),
   101  		},
   102  		{
   103  			name: "int64 zero",
   104  			obj:  int64(math.MinInt64),
   105  		},
   106  		{
   107  			name: "uint64 zero",
   108  			obj:  uint64(0),
   109  		},
   110  		{
   111  			name: "int32 max",
   112  			obj:  int32(math.MaxInt32),
   113  		},
   114  		{
   115  			name: "int32 min",
   116  			obj:  int32(math.MinInt32),
   117  		},
   118  		{
   119  			name: "int32 zero",
   120  			obj:  int32(math.MinInt32),
   121  		},
   122  		{
   123  			name: "uint32 max",
   124  			obj:  uint32(math.MaxUint32),
   125  		},
   126  		{
   127  			name: "uint32 zero",
   128  			obj:  uint32(0),
   129  		},
   130  		{
   131  			name: "int16 max",
   132  			obj:  int16(math.MaxInt16),
   133  		},
   134  		{
   135  			name: "int16 min",
   136  			obj:  int16(math.MinInt16),
   137  		},
   138  		{
   139  			name: "int16 zero",
   140  			obj:  int16(math.MinInt16),
   141  		},
   142  		{
   143  			name: "uint16 max",
   144  			obj:  uint16(math.MaxUint16),
   145  		},
   146  		{
   147  			name: "uint16 zero",
   148  			obj:  uint16(0),
   149  		},
   150  		{
   151  			name: "int8 max",
   152  			obj:  int8(math.MaxInt8),
   153  		},
   154  		{
   155  			name: "int8 min",
   156  			obj:  int8(math.MinInt8),
   157  		},
   158  		{
   159  			name: "int8 zero",
   160  			obj:  int8(math.MinInt8),
   161  		},
   162  		{
   163  			name: "uint8 max",
   164  			obj:  uint8(math.MaxUint8),
   165  		},
   166  		{
   167  			name: "uint8 zero",
   168  			obj:  uint8(0),
   169  		},
   170  		{
   171  			name: "float64",
   172  			obj:  float64(2.71),
   173  		},
   174  		{
   175  			name: "float64 max",
   176  			obj:  float64(math.MaxFloat64),
   177  		},
   178  		{
   179  			name: "float64 smallest nonzero",
   180  			obj:  float64(math.SmallestNonzeroFloat64),
   181  		},
   182  		{
   183  			name: "float64 no fractional component",
   184  			obj:  float64(5),
   185  		},
   186  		{
   187  			name: "float32",
   188  			obj:  float32(2.71),
   189  		},
   190  		{
   191  			name: "float32 max",
   192  			obj:  float32(math.MaxFloat32),
   193  		},
   194  		{
   195  			name: "float32 smallest nonzero",
   196  			obj:  float32(math.SmallestNonzeroFloat32),
   197  		},
   198  		{
   199  			name: "float32 no fractional component",
   200  			obj:  float32(5),
   201  		},
   202  		{
   203  			name: "time.Time",
   204  			obj:  time.Date(2222, time.May, 4, 12, 13, 14, 123, time.UTC),
   205  		},
   206  		{
   207  			name: "int64 omitempty",
   208  			obj: struct {
   209  				V int64 `json:"v,omitempty"`
   210  			}{},
   211  		},
   212  		{
   213  			name: "float64 omitempty",
   214  			obj: struct {
   215  				V float64 `json:"v,omitempty"`
   216  			}{},
   217  		},
   218  		{
   219  			name: "string omitempty",
   220  			obj: struct {
   221  				V string `json:"v,omitempty"`
   222  			}{},
   223  		},
   224  		{
   225  			name: "bool omitempty",
   226  			obj: struct {
   227  				V bool `json:"v,omitempty"`
   228  			}{},
   229  		},
   230  		{
   231  			name: "nil pointer omitempty",
   232  			obj: struct {
   233  				V *struct{} `json:"v,omitempty"`
   234  			}{},
   235  		},
   236  		{
   237  			name: "nil pointer to slice as struct field",
   238  			obj: struct {
   239  				V *[]interface{} `json:"v"`
   240  			}{},
   241  		},
   242  		{
   243  			name: "nil pointer to slice as struct field with omitempty",
   244  			obj: struct {
   245  				V *[]interface{} `json:"v,omitempty"`
   246  			}{},
   247  		},
   248  		{
   249  			name: "nil pointer to map as struct field",
   250  			obj: struct {
   251  				V *map[string]interface{} `json:"v"`
   252  			}{},
   253  		},
   254  		{
   255  			name: "nil pointer to map as struct field with omitempty",
   256  			obj: struct {
   257  				V *map[string]interface{} `json:"v,omitempty"`
   258  			}{},
   259  		},
   260  	} {
   261  		modePairs := tc.modePairs
   262  		if len(modePairs) == 0 {
   263  			// Default is all modes to all modes.
   264  			modePairs = []modePair{}
   265  			for _, encMode := range allEncModes {
   266  				for _, decMode := range allDecModes {
   267  					modePairs = append(modePairs, modePair{enc: encMode, dec: decMode})
   268  				}
   269  			}
   270  		}
   271  
   272  		for _, modePair := range modePairs {
   273  			encModeName, ok := encModeNames[modePair.enc]
   274  			if !ok {
   275  				t.Fatal("test case configured to run against unrecognized encode mode")
   276  			}
   277  
   278  			decModeName, ok := decModeNames[modePair.dec]
   279  			if !ok {
   280  				t.Fatal("test case configured to run against unrecognized decode mode")
   281  			}
   282  
   283  			t.Run(fmt.Sprintf("enc=%s/dec=%s/%s", encModeName, decModeName, tc.name), func(t *testing.T) {
   284  				original := tc.obj
   285  
   286  				cborFromOriginal, err := modePair.enc.Marshal(original)
   287  				if err != nil {
   288  					t.Fatalf("unexpected error from Marshal of original: %v", err)
   289  				}
   290  
   291  				var iface interface{}
   292  				if err := modePair.dec.Unmarshal(cborFromOriginal, &iface); err != nil {
   293  					t.Fatalf("unexpected error from Unmarshal into %T: %v", &iface, err)
   294  				}
   295  
   296  				cborFromIface, err := modePair.enc.Marshal(iface)
   297  				if err != nil {
   298  					t.Fatalf("unexpected error from Marshal of iface: %v", err)
   299  				}
   300  
   301  				{
   302  					// interface{} to interface{}
   303  					var iface2 interface{}
   304  					if err := modePair.dec.Unmarshal(cborFromIface, &iface2); err != nil {
   305  						t.Fatalf("unexpected error from Unmarshal into %T: %v", &iface2, err)
   306  					}
   307  					if diff := cmp.Diff(iface, iface2); diff != "" {
   308  						t.Errorf("unexpected difference on roundtrip from interface{} to interface{}:\n%s", diff)
   309  					}
   310  				}
   311  
   312  				{
   313  					// original to original
   314  					final := reflect.New(reflect.TypeOf(original))
   315  					err = modePair.dec.Unmarshal(cborFromOriginal, final.Interface())
   316  					if err != nil {
   317  						t.Fatalf("unexpected error from Unmarshal into %T: %v", final.Interface(), err)
   318  					}
   319  					if diff := cmp.Diff(original, final.Elem().Interface()); diff != "" {
   320  						t.Errorf("unexpected difference on roundtrip from original to original:\n%s", diff)
   321  					}
   322  				}
   323  
   324  				{
   325  					// original to interface{} to original
   326  					finalViaIface := reflect.New(reflect.TypeOf(original))
   327  					err = modePair.dec.Unmarshal(cborFromIface, finalViaIface.Interface())
   328  					if err != nil {
   329  						t.Fatalf("unexpected error from Unmarshal into %T: %v", finalViaIface.Interface(), err)
   330  					}
   331  					if diff := cmp.Diff(original, finalViaIface.Elem().Interface()); diff != "" {
   332  						t.Errorf("unexpected difference on roundtrip from original to interface{} to original:\n%s", diff)
   333  					}
   334  				}
   335  			})
   336  		}
   337  	}
   338  }
   339  

View as plain text