github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/test/runtime_serializer_protobuf_protobuf_test.go (about) 1 /* 2 Copyright 2015 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 test 18 19 import ( 20 "bytes" 21 "encoding/hex" 22 "fmt" 23 "reflect" 24 "strings" 25 "testing" 26 27 "github.com/stretchr/testify/require" 28 29 apiequality "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/api/equality" 30 metav1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1" 31 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/testapigroup/v1" 32 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime" 33 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema" 34 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/serializer/protobuf" 35 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/diff" 36 ) 37 38 type testObject struct { 39 gvk schema.GroupVersionKind 40 } 41 42 func (d *testObject) GetObjectKind() schema.ObjectKind { return d } 43 func (d *testObject) SetGroupVersionKind(gvk schema.GroupVersionKind) { d.gvk = gvk } 44 func (d *testObject) GroupVersionKind() schema.GroupVersionKind { return d.gvk } 45 func (d *testObject) DeepCopyObject() runtime.Object { 46 panic("testObject does not support DeepCopy") 47 } 48 49 type testMarshalable struct { 50 testObject 51 data []byte 52 err error 53 } 54 55 func (d *testMarshalable) Marshal() ([]byte, error) { 56 return d.data, d.err 57 } 58 59 func (d *testMarshalable) DeepCopyObject() runtime.Object { 60 panic("testMarshalable does not support DeepCopy") 61 } 62 63 type testBufferedMarshalable struct { 64 testObject 65 data []byte 66 err error 67 } 68 69 func (d *testBufferedMarshalable) Marshal() ([]byte, error) { 70 return nil, fmt.Errorf("not invokable") 71 } 72 73 func (d *testBufferedMarshalable) MarshalTo(data []byte) (int, error) { 74 copy(data, d.data) 75 return len(d.data), d.err 76 } 77 78 func (d *testBufferedMarshalable) Size() int { 79 return len(d.data) 80 } 81 82 func (d *testBufferedMarshalable) DeepCopyObject() runtime.Object { 83 panic("testBufferedMarshalable does not support DeepCopy") 84 } 85 86 func TestRecognize(t *testing.T) { 87 s := protobuf.NewSerializer(nil, nil) 88 ignores := [][]byte{ 89 nil, 90 {}, 91 []byte("k8s"), 92 {0x6b, 0x38, 0x73, 0x01}, 93 } 94 for i, data := range ignores { 95 if ok, _, err := s.RecognizesData(data); err != nil || ok { 96 t.Errorf("%d: should not recognize data: %v", i, err) 97 } 98 } 99 recognizes := [][]byte{ 100 {0x6b, 0x38, 0x73, 0x00}, 101 {0x6b, 0x38, 0x73, 0x00, 0x01}, 102 } 103 for i, data := range recognizes { 104 if ok, _, err := s.RecognizesData(data); err != nil || !ok { 105 t.Errorf("%d: should recognize data: %v", i, err) 106 } 107 } 108 } 109 110 func TestEncode(t *testing.T) { 111 obj1 := &testMarshalable{testObject: testObject{}, data: []byte{}} 112 wire1 := []byte{ 113 0x6b, 0x38, 0x73, 0x00, // prefix 114 0x0a, 0x04, 115 0x0a, 0x00, // apiversion 116 0x12, 0x00, // kind 117 0x12, 0x00, // data 118 0x1a, 0x00, // content-type 119 0x22, 0x00, // content-encoding 120 } 121 obj2 := &testMarshalable{ 122 testObject: testObject{gvk: schema.GroupVersionKind{Kind: "test", Group: "other", Version: "version"}}, 123 data: []byte{0x01, 0x02, 0x03}, 124 } 125 wire2 := []byte{ 126 0x6b, 0x38, 0x73, 0x00, // prefix 127 0x0a, 0x15, 128 0x0a, 0x0d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, // apiversion 129 0x12, 0x04, 0x74, 0x65, 0x73, 0x74, // kind 130 0x12, 0x03, 0x01, 0x02, 0x03, // data 131 0x1a, 0x00, // content-type 132 0x22, 0x00, // content-encoding 133 } 134 135 err1 := fmt.Errorf("a test error") 136 137 testCases := []struct { 138 obj runtime.Object 139 data []byte 140 errFn func(error) bool 141 }{ 142 { 143 obj: &testObject{}, 144 errFn: protobuf.IsNotMarshalable, 145 }, 146 { 147 obj: obj1, 148 data: wire1, 149 }, 150 { 151 obj: &testMarshalable{testObject: obj1.testObject, err: err1}, 152 errFn: func(err error) bool { return err == err1 }, 153 }, 154 { 155 // if this test fails, writing the "fast path" marshal is not the same as the "slow path" 156 obj: &testBufferedMarshalable{testObject: obj1.testObject, data: obj1.data}, 157 data: wire1, 158 }, 159 { 160 obj: obj2, 161 data: wire2, 162 }, 163 { 164 // if this test fails, writing the "fast path" marshal is not the same as the "slow path" 165 obj: &testBufferedMarshalable{testObject: obj2.testObject, data: obj2.data}, 166 data: wire2, 167 }, 168 { 169 obj: &testBufferedMarshalable{testObject: obj1.testObject, err: err1}, 170 errFn: func(err error) bool { return err == err1 }, 171 }, 172 } 173 174 for i, test := range testCases { 175 s := protobuf.NewSerializer(nil, nil) 176 data, err := runtime.Encode(s, test.obj) 177 178 switch { 179 case err == nil && test.errFn != nil: 180 t.Errorf("%d: failed: %v", i, err) 181 continue 182 case err != nil && test.errFn == nil: 183 t.Errorf("%d: failed: %v", i, err) 184 continue 185 case err != nil: 186 if !test.errFn(err) { 187 t.Errorf("%d: failed: %v", i, err) 188 } 189 if data != nil { 190 t.Errorf("%d: should not have returned nil data", i) 191 } 192 continue 193 } 194 195 if test.data != nil && !bytes.Equal(test.data, data) { 196 t.Errorf("%d: unexpected data:\n%s", i, hex.Dump(data)) 197 continue 198 } 199 200 if ok, _, err := s.RecognizesData(data); !ok || err != nil { 201 t.Errorf("%d: did not recognize data generated by call: %v", i, err) 202 } 203 } 204 } 205 206 func TestProtobufDecode(t *testing.T) { 207 wire1 := []byte{ 208 0x6b, 0x38, 0x73, 0x00, // prefix 209 0x0a, 0x04, 210 0x0a, 0x00, // apiversion 211 0x12, 0x00, // kind 212 0x12, 0x00, // data 213 0x1a, 0x00, // content-type 214 0x22, 0x00, // content-encoding 215 } 216 wire2 := []byte{ 217 0x6b, 0x38, 0x73, 0x00, // prefix 218 0x0a, 0x15, 219 0x0a, 0x0d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, // apiversion 220 0x12, 0x04, 0x74, 0x65, 0x73, 0x74, // kind 221 0x12, 0x07, 0x6b, 0x38, 0x73, 0x00, 0x01, 0x02, 0x03, // data 222 0x1a, 0x00, // content-type 223 0x22, 0x00, // content-encoding 224 } 225 226 testCases := []struct { 227 obj runtime.Object 228 data []byte 229 errFn func(error) bool 230 }{ 231 { 232 obj: &runtime.Unknown{}, 233 errFn: func(err error) bool { return err.Error() == "empty data" }, 234 }, 235 { 236 data: []byte{0x6b}, 237 errFn: func(err error) bool { return strings.Contains(err.Error(), "does not appear to be a protobuf message") }, 238 }, 239 { 240 obj: &runtime.Unknown{ 241 Raw: []byte{}, 242 }, 243 data: wire1, 244 }, 245 { 246 obj: &runtime.Unknown{ 247 TypeMeta: runtime.TypeMeta{ 248 APIVersion: "other/version", 249 Kind: "test", 250 }, 251 // content type is set because the prefix matches the content 252 ContentType: runtime.ContentTypeProtobuf, 253 Raw: []byte{0x6b, 0x38, 0x73, 0x00, 0x01, 0x02, 0x03}, 254 }, 255 data: wire2, 256 }, 257 } 258 259 for i, test := range testCases { 260 s := protobuf.NewSerializer(nil, nil) 261 unk := &runtime.Unknown{} 262 err := runtime.DecodeInto(s, test.data, unk) 263 264 switch { 265 case err == nil && test.errFn != nil: 266 t.Errorf("%d: failed: %v", i, err) 267 continue 268 case err != nil && test.errFn == nil: 269 t.Errorf("%d: failed: %v", i, err) 270 continue 271 case err != nil: 272 if !test.errFn(err) { 273 t.Errorf("%d: failed: %v", i, err) 274 } 275 continue 276 } 277 278 if !reflect.DeepEqual(unk, test.obj) { 279 t.Errorf("%d: unexpected object:\n%#v", i, unk) 280 continue 281 } 282 } 283 } 284 285 func TestDecodeObjects(t *testing.T) { 286 obj1 := &v1.Carp{ 287 ObjectMeta: metav1.ObjectMeta{ 288 Name: "cool", 289 }, 290 Spec: v1.CarpSpec{ 291 Hostname: "coolhost", 292 }, 293 } 294 obj1wire, err := obj1.Marshal() 295 if err != nil { 296 t.Fatal(err) 297 } 298 299 wire1, err := (&runtime.Unknown{ 300 TypeMeta: runtime.TypeMeta{Kind: "Carp", APIVersion: "v1"}, 301 Raw: obj1wire, 302 }).Marshal() 303 if err != nil { 304 t.Fatal(err) 305 } 306 307 unk2 := &runtime.Unknown{ 308 TypeMeta: runtime.TypeMeta{Kind: "Carp", APIVersion: "v1"}, 309 } 310 wire2 := make([]byte, len(wire1)*2) 311 n, err := unk2.NestedMarshalTo(wire2, obj1, uint64(obj1.Size())) 312 if err != nil { 313 t.Fatal(err) 314 } 315 if n != len(wire1) || !bytes.Equal(wire1, wire2[:n]) { 316 t.Fatalf("unexpected wire:\n%s", hex.Dump(wire2[:n])) 317 } 318 319 wire1 = append([]byte{0x6b, 0x38, 0x73, 0x00}, wire1...) 320 321 obj1WithKind := obj1.DeepCopyObject() 322 obj1WithKind.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Carp"}) 323 testCases := []struct { 324 obj runtime.Object 325 data []byte 326 errFn func(error) bool 327 }{ 328 { 329 obj: obj1WithKind, 330 data: wire1, 331 }, 332 } 333 scheme := runtime.NewScheme() 334 for i, test := range testCases { 335 scheme.AddKnownTypes(schema.GroupVersion{Version: "v1"}, &v1.Carp{}) 336 require.NoError(t, v1.AddToScheme(scheme)) 337 s := protobuf.NewSerializer(scheme, scheme) 338 obj, err := runtime.Decode(s, test.data) 339 340 switch { 341 case err == nil && test.errFn != nil: 342 t.Errorf("%d: failed: %v", i, err) 343 continue 344 case err != nil && test.errFn == nil: 345 t.Errorf("%d: failed: %v", i, err) 346 continue 347 case err != nil: 348 if !test.errFn(err) { 349 t.Errorf("%d: failed: %v", i, err) 350 } 351 if obj != nil { 352 t.Errorf("%d: should not have returned an object", i) 353 } 354 continue 355 } 356 357 if !apiequality.Semantic.DeepEqual(obj, test.obj) { 358 t.Errorf("%d: unexpected object:\n%s", i, diff.ObjectGoPrintDiff(test.obj, obj)) 359 continue 360 } 361 } 362 }