serialization_test.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package testing
  14. import (
  15. "bytes"
  16. "encoding/hex"
  17. gojson "encoding/json"
  18. "io/ioutil"
  19. "math/rand"
  20. "reflect"
  21. "testing"
  22. jsoniter "github.com/json-iterator/go"
  23. appsv1 "k8s.io/api/apps/v1"
  24. "k8s.io/api/core/v1"
  25. "k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
  26. "k8s.io/apimachinery/pkg/api/apitesting/roundtrip"
  27. apiequality "k8s.io/apimachinery/pkg/api/equality"
  28. "k8s.io/apimachinery/pkg/api/meta"
  29. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  30. "k8s.io/apimachinery/pkg/conversion"
  31. "k8s.io/apimachinery/pkg/runtime"
  32. "k8s.io/apimachinery/pkg/runtime/schema"
  33. "k8s.io/apimachinery/pkg/runtime/serializer/json"
  34. "k8s.io/apimachinery/pkg/runtime/serializer/streaming"
  35. "k8s.io/apimachinery/pkg/util/diff"
  36. "k8s.io/apimachinery/pkg/util/sets"
  37. "k8s.io/apimachinery/pkg/watch"
  38. "k8s.io/kubernetes/pkg/api/legacyscheme"
  39. "k8s.io/kubernetes/pkg/api/testapi"
  40. "k8s.io/kubernetes/pkg/apis/apps"
  41. k8s_apps_v1 "k8s.io/kubernetes/pkg/apis/apps/v1"
  42. api "k8s.io/kubernetes/pkg/apis/core"
  43. k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
  44. )
  45. // fuzzInternalObject fuzzes an arbitrary runtime object using the appropriate
  46. // fuzzer registered with the apitesting package.
  47. func fuzzInternalObject(t *testing.T, forVersion schema.GroupVersion, item runtime.Object, seed int64) runtime.Object {
  48. fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(seed), legacyscheme.Codecs).Fuzz(item)
  49. j, err := meta.TypeAccessor(item)
  50. if err != nil {
  51. t.Fatalf("Unexpected error %v for %#v", err, item)
  52. }
  53. j.SetKind("")
  54. j.SetAPIVersion("")
  55. return item
  56. }
  57. func ConvertV1ReplicaSetToAPIReplicationController(in *appsv1.ReplicaSet, out *api.ReplicationController, s conversion.Scope) error {
  58. intermediate1 := &apps.ReplicaSet{}
  59. if err := k8s_apps_v1.Convert_v1_ReplicaSet_To_apps_ReplicaSet(in, intermediate1, s); err != nil {
  60. return err
  61. }
  62. intermediate2 := &v1.ReplicationController{}
  63. if err := k8s_api_v1.Convert_apps_ReplicaSet_To_v1_ReplicationController(intermediate1, intermediate2, s); err != nil {
  64. return err
  65. }
  66. return k8s_api_v1.Convert_v1_ReplicationController_To_core_ReplicationController(intermediate2, out, s)
  67. }
  68. func TestSetControllerConversion(t *testing.T) {
  69. if err := legacyscheme.Scheme.AddConversionFuncs(ConvertV1ReplicaSetToAPIReplicationController); err != nil {
  70. t.Fatal(err)
  71. }
  72. rs := &apps.ReplicaSet{}
  73. rc := &api.ReplicationController{}
  74. extGroup := schema.GroupVersion{Group: "apps", Version: "v1"}
  75. extCodec := legacyscheme.Codecs.LegacyCodec(extGroup)
  76. defaultGroup := schema.GroupVersion{Group: "", Version: "v1"}
  77. defaultCodec := legacyscheme.Codecs.LegacyCodec(defaultGroup)
  78. fuzzInternalObject(t, schema.GroupVersion{Group: "apps", Version: runtime.APIVersionInternal}, rs, rand.Int63())
  79. // explicitly set the selector to something that is convertible to old-style selectors
  80. // (since normally we'll fuzz the selectors with things that aren't convertible)
  81. rs.Spec.Selector = &metav1.LabelSelector{
  82. MatchLabels: map[string]string{
  83. "foo": "bar",
  84. "baz": "quux",
  85. },
  86. }
  87. t.Logf("rs._internal.apps -> rs.v1.apps")
  88. data, err := runtime.Encode(extCodec, rs)
  89. if err != nil {
  90. t.Fatalf("unexpected encoding error: %v", err)
  91. }
  92. decoder := legacyscheme.Codecs.DecoderToVersion(
  93. legacyscheme.Codecs.UniversalDeserializer(),
  94. runtime.NewMultiGroupVersioner(
  95. defaultGroup,
  96. schema.GroupKind{Group: defaultGroup.Group},
  97. schema.GroupKind{Group: extGroup.Group},
  98. ),
  99. )
  100. t.Logf("rs.v1.apps -> rc._internal")
  101. if err := runtime.DecodeInto(decoder, data, rc); err != nil {
  102. t.Fatalf("unexpected decoding error: %v", err)
  103. }
  104. t.Logf("rc._internal -> rc.v1")
  105. data, err = runtime.Encode(defaultCodec, rc)
  106. if err != nil {
  107. t.Fatalf("unexpected encoding error: %v", err)
  108. }
  109. t.Logf("rc.v1 -> rs._internal.apps")
  110. if err := runtime.DecodeInto(decoder, data, rs); err != nil {
  111. t.Fatalf("unexpected decoding error: %v", err)
  112. }
  113. }
  114. // TestSpecificKind round-trips a single specific kind and is intended to help
  115. // debug issues that arise while adding a new API type.
  116. func TestSpecificKind(t *testing.T) {
  117. // Uncomment the following line to enable logging of which conversions
  118. // legacyscheme.Scheme.Log(t)
  119. internalGVK := schema.GroupVersionKind{Group: "apps", Version: runtime.APIVersionInternal, Kind: "DaemonSet"}
  120. seed := rand.Int63()
  121. fuzzer := fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(seed), legacyscheme.Codecs)
  122. roundtrip.RoundTripSpecificKind(t, internalGVK, legacyscheme.Scheme, legacyscheme.Codecs, fuzzer, nil)
  123. }
  124. var nonRoundTrippableTypes = sets.NewString(
  125. "ExportOptions",
  126. "GetOptions",
  127. // WatchEvent does not include kind and version and can only be deserialized
  128. // implicitly (if the caller expects the specific object). The watch call defines
  129. // the schema by content type, rather than via kind/version included in each
  130. // object.
  131. "WatchEvent",
  132. // ListOptions is now part of the meta group
  133. "ListOptions",
  134. // DeleteOptions, CreateOptions and UpdateOptions are only read in metav1
  135. "DeleteOptions",
  136. "CreateOptions",
  137. "UpdateOptions",
  138. "PatchOptions",
  139. )
  140. var commonKinds = []string{"Status", "ListOptions", "DeleteOptions", "ExportOptions", "GetOptions", "CreateOptions", "UpdateOptions", "PatchOptions"}
  141. // TestCommonKindsRegistered verifies that all group/versions registered with
  142. // the testapi package have the common kinds.
  143. func TestCommonKindsRegistered(t *testing.T) {
  144. for _, kind := range commonKinds {
  145. for _, group := range testapi.Groups {
  146. gv := group.GroupVersion()
  147. gvk := gv.WithKind(kind)
  148. obj, err := legacyscheme.Scheme.New(gvk)
  149. if err != nil {
  150. t.Error(err)
  151. }
  152. defaults := gv.WithKind("")
  153. var got *schema.GroupVersionKind
  154. if obj, got, err = legacyscheme.Codecs.LegacyCodec().Decode([]byte(`{"kind":"`+kind+`"}`), &defaults, obj); err != nil || gvk != *got {
  155. t.Errorf("expected %v: %v %v", gvk, got, err)
  156. }
  157. data, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(*gv), obj)
  158. if err != nil {
  159. t.Errorf("expected %v: %v\n%s", gvk, err, string(data))
  160. continue
  161. }
  162. if !bytes.Contains(data, []byte(`"kind":"`+kind+`","apiVersion":"`+gv.String()+`"`)) {
  163. if kind != "Status" {
  164. t.Errorf("expected %v: %v\n%s", gvk, err, string(data))
  165. continue
  166. }
  167. // TODO: this is wrong, but legacy clients expect it
  168. if !bytes.Contains(data, []byte(`"kind":"`+kind+`","apiVersion":"v1"`)) {
  169. t.Errorf("expected %v: %v\n%s", gvk, err, string(data))
  170. continue
  171. }
  172. }
  173. }
  174. }
  175. }
  176. // TestRoundTripTypes applies the round-trip test to all round-trippable Kinds
  177. // in all of the API groups registered for test in the testapi package.
  178. func TestRoundTripTypes(t *testing.T) {
  179. seed := rand.Int63()
  180. fuzzer := fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(seed), legacyscheme.Codecs)
  181. nonRoundTrippableTypes := map[schema.GroupVersionKind]bool{}
  182. roundtrip.RoundTripTypes(t, legacyscheme.Scheme, legacyscheme.Codecs, fuzzer, nonRoundTrippableTypes)
  183. }
  184. // TestEncodePtr tests that a pointer to a golang type can be encoded and
  185. // decoded without information loss or mutation.
  186. func TestEncodePtr(t *testing.T) {
  187. grace := int64(30)
  188. enableServiceLinks := v1.DefaultEnableServiceLinks
  189. preemptNever := api.PreemptNever
  190. pod := &api.Pod{
  191. ObjectMeta: metav1.ObjectMeta{
  192. Labels: map[string]string{"name": "foo"},
  193. },
  194. Spec: api.PodSpec{
  195. RestartPolicy: api.RestartPolicyAlways,
  196. DNSPolicy: api.DNSClusterFirst,
  197. TerminationGracePeriodSeconds: &grace,
  198. SecurityContext: &api.PodSecurityContext{},
  199. SchedulerName: api.DefaultSchedulerName,
  200. EnableServiceLinks: &enableServiceLinks,
  201. PreemptionPolicy: &preemptNever,
  202. },
  203. }
  204. obj := runtime.Object(pod)
  205. data, err := runtime.Encode(testapi.Default.Codec(), obj)
  206. obj2, err2 := runtime.Decode(testapi.Default.Codec(), data)
  207. if err != nil || err2 != nil {
  208. t.Fatalf("Failure: '%v' '%v'", err, err2)
  209. }
  210. if _, ok := obj2.(*api.Pod); !ok {
  211. t.Fatalf("Got wrong type")
  212. }
  213. if !apiequality.Semantic.DeepEqual(obj2, pod) {
  214. t.Errorf("\nExpected:\n\n %#v,\n\nGot:\n\n %#vDiff: %v\n\n", pod, obj2, diff.ObjectDiff(obj2, pod))
  215. }
  216. }
  217. func TestDecodeTimeStampWithoutQuotes(t *testing.T) {
  218. testYAML := []byte(`
  219. apiVersion: v1
  220. kind: Pod
  221. metadata:
  222. creationTimestamp: 2018-08-30T14:10:58Z
  223. name: test
  224. spec:
  225. containers: null
  226. status: {}`)
  227. if obj, err := runtime.Decode(testapi.Default.Codec(), testYAML); err != nil {
  228. t.Fatalf("unable to decode yaml: %v", err)
  229. } else {
  230. if obj2, ok := obj.(*api.Pod); !ok {
  231. t.Fatalf("Got wrong type")
  232. } else {
  233. if obj2.ObjectMeta.CreationTimestamp.UnixNano() != parseTimeOrDie("2018-08-30T14:10:58Z").UnixNano() {
  234. t.Fatalf("Time stamps do not match")
  235. }
  236. }
  237. }
  238. }
  239. // TestBadJSONRejection establishes that a JSON object without a kind or with
  240. // an unknown kind will not be decoded without error.
  241. func TestBadJSONRejection(t *testing.T) {
  242. badJSONMissingKind := []byte(`{ }`)
  243. if _, err := runtime.Decode(testapi.Default.Codec(), badJSONMissingKind); err == nil {
  244. t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind)
  245. }
  246. badJSONUnknownType := []byte(`{"kind": "bar"}`)
  247. if _, err1 := runtime.Decode(testapi.Default.Codec(), badJSONUnknownType); err1 == nil {
  248. t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType)
  249. }
  250. /*badJSONKindMismatch := []byte(`{"kind": "Pod"}`)
  251. if err2 := DecodeInto(badJSONKindMismatch, &Node{}); err2 == nil {
  252. t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch)
  253. }*/
  254. }
  255. // TestUnversionedTypes establishes that the default codec can encode and
  256. // decode unversioned objects.
  257. func TestUnversionedTypes(t *testing.T) {
  258. testcases := []runtime.Object{
  259. &metav1.Status{Status: "Failure", Message: "something went wrong"},
  260. &metav1.APIVersions{Versions: []string{"A", "B", "C"}},
  261. &metav1.APIGroupList{Groups: []metav1.APIGroup{{Name: "mygroup"}}},
  262. &metav1.APIGroup{Name: "mygroup"},
  263. &metav1.APIResourceList{GroupVersion: "mygroup/myversion"},
  264. }
  265. for _, obj := range testcases {
  266. // Make sure the unversioned codec can encode
  267. unversionedJSON, err := runtime.Encode(testapi.Default.Codec(), obj)
  268. if err != nil {
  269. t.Errorf("%v: unexpected error: %v", obj, err)
  270. continue
  271. }
  272. // Make sure the versioned codec under test can decode
  273. versionDecodedObject, err := runtime.Decode(testapi.Default.Codec(), unversionedJSON)
  274. if err != nil {
  275. t.Errorf("%v: unexpected error: %v", obj, err)
  276. continue
  277. }
  278. // Make sure it decodes correctly
  279. if !reflect.DeepEqual(obj, versionDecodedObject) {
  280. t.Errorf("%v: expected %#v, got %#v", obj, obj, versionDecodedObject)
  281. continue
  282. }
  283. }
  284. }
  285. // TestObjectWatchFraming establishes that a watch event can be encoded and
  286. // decoded correctly through each of the supported RFC2046 media types.
  287. func TestObjectWatchFraming(t *testing.T) {
  288. f := fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(benchmarkSeed), legacyscheme.Codecs)
  289. secret := &api.Secret{}
  290. f.Fuzz(secret)
  291. if secret.Data == nil {
  292. secret.Data = map[string][]byte{}
  293. }
  294. secret.Data["binary"] = []byte{0x00, 0x10, 0x30, 0x55, 0xff, 0x00}
  295. secret.Data["utf8"] = []byte("a string with \u0345 characters")
  296. secret.Data["long"] = bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x00}, 1000)
  297. converted, _ := legacyscheme.Scheme.ConvertToVersion(secret, v1.SchemeGroupVersion)
  298. v1secret := converted.(*v1.Secret)
  299. for _, info := range legacyscheme.Codecs.SupportedMediaTypes() {
  300. if info.StreamSerializer == nil {
  301. continue
  302. }
  303. s := info.StreamSerializer
  304. framer := s.Framer
  305. embedded := info.Serializer
  306. if embedded == nil {
  307. t.Errorf("no embedded serializer for %s", info.MediaType)
  308. continue
  309. }
  310. innerDecode := legacyscheme.Codecs.DecoderToVersion(embedded, api.SchemeGroupVersion)
  311. // write a single object through the framer and back out
  312. obj := &bytes.Buffer{}
  313. if err := s.Encode(v1secret, obj); err != nil {
  314. t.Fatal(err)
  315. }
  316. out := &bytes.Buffer{}
  317. w := framer.NewFrameWriter(out)
  318. if n, err := w.Write(obj.Bytes()); err != nil || n != len(obj.Bytes()) {
  319. t.Fatal(err)
  320. }
  321. sr := streaming.NewDecoder(framer.NewFrameReader(ioutil.NopCloser(out)), s)
  322. resultSecret := &v1.Secret{}
  323. res, _, err := sr.Decode(nil, resultSecret)
  324. if err != nil {
  325. t.Fatalf("%v:\n%s", err, hex.Dump(obj.Bytes()))
  326. }
  327. resultSecret.Kind = "Secret"
  328. resultSecret.APIVersion = "v1"
  329. if !apiequality.Semantic.DeepEqual(v1secret, res) {
  330. t.Fatalf("objects did not match: %s", diff.ObjectGoPrintDiff(v1secret, res))
  331. }
  332. // write a watch event through the frame writer and read it back in
  333. // via the frame reader for this media type
  334. obj = &bytes.Buffer{}
  335. if err := embedded.Encode(v1secret, obj); err != nil {
  336. t.Fatal(err)
  337. }
  338. event := &metav1.WatchEvent{Type: string(watch.Added)}
  339. event.Object.Raw = obj.Bytes()
  340. obj = &bytes.Buffer{}
  341. if err := s.Encode(event, obj); err != nil {
  342. t.Fatal(err)
  343. }
  344. out = &bytes.Buffer{}
  345. w = framer.NewFrameWriter(out)
  346. if n, err := w.Write(obj.Bytes()); err != nil || n != len(obj.Bytes()) {
  347. t.Fatal(err)
  348. }
  349. sr = streaming.NewDecoder(framer.NewFrameReader(ioutil.NopCloser(out)), s)
  350. outEvent := &metav1.WatchEvent{}
  351. _, _, err = sr.Decode(nil, outEvent)
  352. if err != nil || outEvent.Type != string(watch.Added) {
  353. t.Fatalf("%v: %#v", err, outEvent)
  354. }
  355. if outEvent.Object.Object == nil && outEvent.Object.Raw != nil {
  356. outEvent.Object.Object, err = runtime.Decode(innerDecode, outEvent.Object.Raw)
  357. if err != nil {
  358. t.Fatalf("%v:\n%s", err, hex.Dump(outEvent.Object.Raw))
  359. }
  360. }
  361. if !apiequality.Semantic.DeepEqual(secret, outEvent.Object.Object) {
  362. t.Fatalf("%s: did not match after frame decoding: %s", info.MediaType, diff.ObjectGoPrintDiff(secret, outEvent.Object.Object))
  363. }
  364. }
  365. }
  366. const benchmarkSeed = 100
  367. func benchmarkItems(b *testing.B) []v1.Pod {
  368. apiObjectFuzzer := fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(benchmarkSeed), legacyscheme.Codecs)
  369. items := make([]v1.Pod, 10)
  370. for i := range items {
  371. var pod api.Pod
  372. apiObjectFuzzer.Fuzz(&pod)
  373. pod.Spec.InitContainers, pod.Status.InitContainerStatuses = nil, nil
  374. out, err := legacyscheme.Scheme.ConvertToVersion(&pod, v1.SchemeGroupVersion)
  375. if err != nil {
  376. panic(err)
  377. }
  378. items[i] = *out.(*v1.Pod)
  379. }
  380. return items
  381. }
  382. // BenchmarkEncodeCodec measures the cost of performing a codec encode, which includes
  383. // reflection (to clear APIVersion and Kind)
  384. func BenchmarkEncodeCodec(b *testing.B) {
  385. items := benchmarkItems(b)
  386. width := len(items)
  387. b.ResetTimer()
  388. for i := 0; i < b.N; i++ {
  389. if _, err := runtime.Encode(testapi.Default.Codec(), &items[i%width]); err != nil {
  390. b.Fatal(err)
  391. }
  392. }
  393. b.StopTimer()
  394. }
  395. // BenchmarkEncodeCodecFromInternal measures the cost of performing a codec encode,
  396. // including conversions.
  397. func BenchmarkEncodeCodecFromInternal(b *testing.B) {
  398. items := benchmarkItems(b)
  399. width := len(items)
  400. encodable := make([]api.Pod, width)
  401. for i := range items {
  402. if err := legacyscheme.Scheme.Convert(&items[i], &encodable[i], nil); err != nil {
  403. b.Fatal(err)
  404. }
  405. }
  406. b.ResetTimer()
  407. for i := 0; i < b.N; i++ {
  408. if _, err := runtime.Encode(testapi.Default.Codec(), &encodable[i%width]); err != nil {
  409. b.Fatal(err)
  410. }
  411. }
  412. b.StopTimer()
  413. }
  414. // BenchmarkEncodeJSONMarshal provides a baseline for regular JSON encode performance
  415. func BenchmarkEncodeJSONMarshal(b *testing.B) {
  416. items := benchmarkItems(b)
  417. width := len(items)
  418. b.ResetTimer()
  419. for i := 0; i < b.N; i++ {
  420. if _, err := gojson.Marshal(&items[i%width]); err != nil {
  421. b.Fatal(err)
  422. }
  423. }
  424. b.StopTimer()
  425. }
  426. func BenchmarkDecodeCodec(b *testing.B) {
  427. codec := testapi.Default.Codec()
  428. items := benchmarkItems(b)
  429. width := len(items)
  430. encoded := make([][]byte, width)
  431. for i := range items {
  432. data, err := runtime.Encode(codec, &items[i])
  433. if err != nil {
  434. b.Fatal(err)
  435. }
  436. encoded[i] = data
  437. }
  438. b.ResetTimer()
  439. for i := 0; i < b.N; i++ {
  440. if _, err := runtime.Decode(codec, encoded[i%width]); err != nil {
  441. b.Fatal(err)
  442. }
  443. }
  444. b.StopTimer()
  445. }
  446. func BenchmarkDecodeIntoExternalCodec(b *testing.B) {
  447. codec := testapi.Default.Codec()
  448. items := benchmarkItems(b)
  449. width := len(items)
  450. encoded := make([][]byte, width)
  451. for i := range items {
  452. data, err := runtime.Encode(codec, &items[i])
  453. if err != nil {
  454. b.Fatal(err)
  455. }
  456. encoded[i] = data
  457. }
  458. b.ResetTimer()
  459. for i := 0; i < b.N; i++ {
  460. obj := v1.Pod{}
  461. if err := runtime.DecodeInto(codec, encoded[i%width], &obj); err != nil {
  462. b.Fatal(err)
  463. }
  464. }
  465. b.StopTimer()
  466. }
  467. func BenchmarkDecodeIntoInternalCodec(b *testing.B) {
  468. codec := testapi.Default.Codec()
  469. items := benchmarkItems(b)
  470. width := len(items)
  471. encoded := make([][]byte, width)
  472. for i := range items {
  473. data, err := runtime.Encode(codec, &items[i])
  474. if err != nil {
  475. b.Fatal(err)
  476. }
  477. encoded[i] = data
  478. }
  479. b.ResetTimer()
  480. for i := 0; i < b.N; i++ {
  481. obj := api.Pod{}
  482. if err := runtime.DecodeInto(codec, encoded[i%width], &obj); err != nil {
  483. b.Fatal(err)
  484. }
  485. }
  486. b.StopTimer()
  487. }
  488. // BenchmarkDecodeJSON provides a baseline for regular JSON decode performance
  489. func BenchmarkDecodeIntoJSON(b *testing.B) {
  490. codec := testapi.Default.Codec()
  491. items := benchmarkItems(b)
  492. width := len(items)
  493. encoded := make([][]byte, width)
  494. for i := range items {
  495. data, err := runtime.Encode(codec, &items[i])
  496. if err != nil {
  497. b.Fatal(err)
  498. }
  499. encoded[i] = data
  500. }
  501. b.ResetTimer()
  502. for i := 0; i < b.N; i++ {
  503. obj := v1.Pod{}
  504. if err := gojson.Unmarshal(encoded[i%width], &obj); err != nil {
  505. b.Fatal(err)
  506. }
  507. }
  508. b.StopTimer()
  509. }
  510. // BenchmarkDecodeIntoJSONCodecGenConfigFast provides a baseline
  511. // for JSON decode performance with jsoniter.ConfigFast
  512. func BenchmarkDecodeIntoJSONCodecGenConfigFast(b *testing.B) {
  513. kcodec := testapi.Default.Codec()
  514. items := benchmarkItems(b)
  515. width := len(items)
  516. encoded := make([][]byte, width)
  517. for i := range items {
  518. data, err := runtime.Encode(kcodec, &items[i])
  519. if err != nil {
  520. b.Fatal(err)
  521. }
  522. encoded[i] = data
  523. }
  524. b.ResetTimer()
  525. for i := 0; i < b.N; i++ {
  526. obj := v1.Pod{}
  527. if err := jsoniter.ConfigFastest.Unmarshal(encoded[i%width], &obj); err != nil {
  528. b.Fatal(err)
  529. }
  530. }
  531. b.StopTimer()
  532. }
  533. // BenchmarkDecodeIntoJSONCodecGenConfigCompatibleWithStandardLibrary provides a
  534. // baseline for JSON decode performance with
  535. // jsoniter.ConfigCompatibleWithStandardLibrary, but with case sensitivity set
  536. // to true
  537. func BenchmarkDecodeIntoJSONCodecGenConfigCompatibleWithStandardLibrary(b *testing.B) {
  538. kcodec := testapi.Default.Codec()
  539. items := benchmarkItems(b)
  540. width := len(items)
  541. encoded := make([][]byte, width)
  542. for i := range items {
  543. data, err := runtime.Encode(kcodec, &items[i])
  544. if err != nil {
  545. b.Fatal(err)
  546. }
  547. encoded[i] = data
  548. }
  549. b.ResetTimer()
  550. iter := json.CaseSensitiveJsonIterator()
  551. for i := 0; i < b.N; i++ {
  552. obj := v1.Pod{}
  553. if err := iter.Unmarshal(encoded[i%width], &obj); err != nil {
  554. b.Fatal(err)
  555. }
  556. }
  557. b.StopTimer()
  558. }