...

Source file src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/pruning/algorithm_test.go

Documentation: k8s.io/apiextensions-apiserver/pkg/apiserver/schema/pruning

     1  /*
     2  Copyright 2019 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 pruning
    18  
    19  import (
    20  	"bytes"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	"k8s.io/apimachinery/pkg/util/json"
    29  
    30  	structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
    31  )
    32  
    33  func TestPrune(t *testing.T) {
    34  	tests := []struct {
    35  		name           string
    36  		json           string
    37  		isResourceRoot bool
    38  		schema         *structuralschema.Structural
    39  		expectedObject string
    40  		expectedPruned []string
    41  	}{
    42  		{name: "empty", json: "null", expectedObject: "null"},
    43  		{name: "scalar", json: "4", schema: &structuralschema.Structural{}, expectedObject: "4"},
    44  		{name: "scalar array", json: "[1,2]", schema: &structuralschema.Structural{
    45  			Items: &structuralschema.Structural{},
    46  		}, expectedObject: "[1,2]"},
    47  		{name: "object array", json: `[{"a":1},{"b":1},{"a":1,"b":2,"c":3}]`, schema: &structuralschema.Structural{
    48  			Items: &structuralschema.Structural{
    49  				Properties: map[string]structuralschema.Structural{
    50  					"a": {},
    51  					"c": {},
    52  				},
    53  			},
    54  		}, expectedObject: `[{"a":1},{},{"a":1,"c":3}]`, expectedPruned: []string{"[1].b", "[2].b"}},
    55  		{name: "object array with nil schema", json: `[{"a":1},{"b":1},{"a":1,"b":2,"c":3}]`, expectedObject: `[{},{},{}]`,
    56  			expectedPruned: []string{"[0].a", "[1].b", "[2].a", "[2].b", "[2].c"}},
    57  		{name: "object array object", json: `{"array":[{"a":1},{"b":1},{"a":1,"b":2,"c":3}],"unspecified":{"a":1},"specified":{"a":1,"b":2,"c":3}}`, schema: &structuralschema.Structural{
    58  			Properties: map[string]structuralschema.Structural{
    59  				"array": {
    60  					Items: &structuralschema.Structural{
    61  						Properties: map[string]structuralschema.Structural{
    62  							"a": {},
    63  							"c": {},
    64  						},
    65  					},
    66  				},
    67  				"specified": {
    68  					Properties: map[string]structuralschema.Structural{
    69  						"a": {},
    70  						"c": {},
    71  					},
    72  				},
    73  			},
    74  		}, expectedObject: `{"array":[{"a":1},{},{"a":1,"c":3}],"specified":{"a":1,"c":3}}`,
    75  			expectedPruned: []string{"array[1].b", "array[2].b", "specified.b", "unspecified"}},
    76  		{name: "nested x-kubernetes-preserve-unknown-fields", json: `
    77  {
    78    "unspecified":"bar",
    79    "alpha": "abc",
    80    "beta": 42.0,
    81    "unspecifiedObject": {"unspecified": "bar"},
    82    "pruning": {
    83       "unspecified": "bar",
    84       "unspecifiedObject": {"unspecified": "bar"},
    85       "pruning": {"unspecified": "bar"},
    86       "apiVersion": "unknown",
    87       "preserving": {"unspecified": "bar"}
    88    },
    89    "preserving": {
    90       "unspecified": "bar",
    91       "unspecifiedObject": {"unspecified": "bar"},
    92       "pruning": {"unspecified": "bar"},
    93       "preserving": {"unspecified": "bar"},
    94       "preservingUnknownType": [{"foo":true},{"bar":true}]
    95    },
    96    "preservingAdditionalPropertiesNotInheritingXPreserveUnknownFields": {
    97       "foo": {
    98          "specified": {"unspecified":"bar"},
    99          "unspecified": "bar"
   100       }
   101    },
   102    "preservingAdditionalPropertiesKeyPruneValues": {
   103       "foo": {
   104          "specified": {"unspecified":"bar"},
   105          "unspecified": "bar"
   106       }
   107    }
   108  }
   109  `, schema: &structuralschema.Structural{
   110  			Generic:    structuralschema.Generic{Type: "object"},
   111  			Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
   112  			Properties: map[string]structuralschema.Structural{
   113  				"alpha": {Generic: structuralschema.Generic{Type: "string"}},
   114  				"beta":  {Generic: structuralschema.Generic{Type: "number"}},
   115  				"pruning": {
   116  					Generic: structuralschema.Generic{Type: "object"},
   117  					Properties: map[string]structuralschema.Structural{
   118  						"preserving": {
   119  							Generic:    structuralschema.Generic{Type: "object"},
   120  							Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
   121  						},
   122  						"pruning": {
   123  							Generic: structuralschema.Generic{Type: "object"},
   124  						},
   125  					},
   126  				},
   127  				"preserving": {
   128  					Generic:    structuralschema.Generic{Type: "object"},
   129  					Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
   130  					Properties: map[string]structuralschema.Structural{
   131  						"preserving": {
   132  							Generic:    structuralschema.Generic{Type: "object"},
   133  							Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
   134  						},
   135  						"preservingUnknownType": {
   136  							Generic:    structuralschema.Generic{Type: ""},
   137  							Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
   138  						},
   139  						"pruning": {
   140  							Generic: structuralschema.Generic{Type: "object"},
   141  						},
   142  					},
   143  				},
   144  				"preservingAdditionalPropertiesNotInheritingXPreserveUnknownFields": {
   145  					// this x-kubernetes-preserve-unknown-fields is not inherited by the schema inside of additionalProperties
   146  					Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
   147  					Generic: structuralschema.Generic{
   148  						Type: "object",
   149  						AdditionalProperties: &structuralschema.StructuralOrBool{
   150  							Structural: &structuralschema.Structural{
   151  								Generic: structuralschema.Generic{Type: "object"},
   152  								Properties: map[string]structuralschema.Structural{
   153  									"specified": {Generic: structuralschema.Generic{Type: "object"}},
   154  								},
   155  							},
   156  						},
   157  					},
   158  				},
   159  				"preservingAdditionalPropertiesKeyPruneValues": {
   160  					Generic: structuralschema.Generic{
   161  						Type: "object",
   162  						AdditionalProperties: &structuralschema.StructuralOrBool{
   163  							Structural: &structuralschema.Structural{
   164  								Generic: structuralschema.Generic{Type: "object"},
   165  								Properties: map[string]structuralschema.Structural{
   166  									"specified": {Generic: structuralschema.Generic{Type: "object"}},
   167  								},
   168  							},
   169  						},
   170  					},
   171  				},
   172  			},
   173  		}, expectedObject: `
   174  {
   175    "unspecified":"bar",
   176    "alpha": "abc",
   177    "beta": 42.0,
   178    "unspecifiedObject": {"unspecified": "bar"},
   179    "pruning": {
   180       "pruning": {},
   181       "preserving": {"unspecified": "bar"}
   182    },
   183    "preserving": {
   184       "unspecified": "bar",
   185       "unspecifiedObject": {"unspecified": "bar"},
   186       "pruning": {},
   187       "preserving": {"unspecified": "bar"},
   188       "preservingUnknownType": [{"foo":true},{"bar":true}]
   189    },
   190    "preservingAdditionalPropertiesNotInheritingXPreserveUnknownFields": {
   191       "foo": {
   192          "specified": {}
   193       }
   194    },
   195    "preservingAdditionalPropertiesKeyPruneValues": {
   196       "foo": {
   197          "specified": {}
   198       }
   199    }
   200  }
   201  `, expectedPruned: []string{"preserving.pruning.unspecified", "preservingAdditionalPropertiesKeyPruneValues.foo.specified.unspecified", "preservingAdditionalPropertiesKeyPruneValues.foo.unspecified", "preservingAdditionalPropertiesNotInheritingXPreserveUnknownFields.foo.specified.unspecified", "preservingAdditionalPropertiesNotInheritingXPreserveUnknownFields.foo.unspecified", "pruning.apiVersion", "pruning.pruning.unspecified", "pruning.unspecified", "pruning.unspecifiedObject"}},
   202  		{name: "additionalProperties with schema", json: `{"a":1,"b":1,"c":{"a":1,"b":2,"c":{"a":1}}}`, schema: &structuralschema.Structural{
   203  			Properties: map[string]structuralschema.Structural{
   204  				"a": {},
   205  				"c": {
   206  					Generic: structuralschema.Generic{
   207  						AdditionalProperties: &structuralschema.StructuralOrBool{
   208  							Structural: &structuralschema.Structural{
   209  								Generic: structuralschema.Generic{
   210  									Type: "integer",
   211  								},
   212  							},
   213  						},
   214  					},
   215  				},
   216  			},
   217  		}, expectedObject: `{"a":1,"c":{"a":1,"b":2,"c":{}}}`,
   218  			expectedPruned: []string{"b", "c.c.a"}},
   219  		{name: "additionalProperties with bool", json: `{"a":1,"b":1,"c":{"a":1,"b":2,"c":{"a":1, "apiVersion": "unknown"}}}`, schema: &structuralschema.Structural{
   220  			Properties: map[string]structuralschema.Structural{
   221  				"a": {},
   222  				"c": {
   223  					Generic: structuralschema.Generic{
   224  						AdditionalProperties: &structuralschema.StructuralOrBool{
   225  							Bool: false,
   226  						},
   227  					},
   228  				},
   229  			},
   230  		}, expectedObject: `{"a":1,"c":{"a":1,"b":2,"c":{}}}`,
   231  			expectedPruned: []string{"b", "c.c.a", "c.c.apiVersion"}},
   232  		{name: "x-kubernetes-embedded-resource", json: `
   233  {
   234    "apiVersion": "foo/v1",
   235    "kind": "Foo",
   236    "metadata": {
   237      "name": "instance",
   238      "unspecified": "bar"
   239    },
   240    "unspecified":"bar",
   241    "pruned": {
   242      "apiVersion": "foo/v1",
   243      "kind": "Foo",
   244      "unspecified": "bar",
   245      "metadata": {
   246        "name": "instance",
   247        "unspecified": "bar"
   248      },
   249      "spec": {
   250        "unspecified": "bar"
   251      }
   252    },
   253    "preserving": {
   254      "apiVersion": "foo/v1",
   255      "kind": "Foo",
   256      "unspecified": "bar",
   257      "metadata": {
   258        "name": "instance",
   259        "unspecified": "bar"
   260      },
   261      "spec": {
   262        "unspecified": "bar"
   263      }
   264    },
   265    "nested": {
   266      "apiVersion": "foo/v1",
   267      "kind": "Foo",
   268      "unspecified": "bar",
   269      "metadata": {
   270        "name": "instance",
   271        "unspecified": "bar"
   272      },
   273      "spec": {
   274        "unspecified": "bar",
   275        "embedded": {
   276          "apiVersion": "foo/v1",
   277          "kind": "Foo",
   278          "unspecified": "bar",
   279          "metadata": {
   280            "name": "instance",
   281            "unspecified": "bar"
   282          },
   283          "spec": {
   284            "unspecified": "bar"
   285          }
   286        }
   287      }
   288    }
   289  }
   290  `, schema: &structuralschema.Structural{
   291  			Generic: structuralschema.Generic{Type: "object"},
   292  			Properties: map[string]structuralschema.Structural{
   293  				"pruned": {
   294  					Generic: structuralschema.Generic{Type: "object"},
   295  					Extensions: structuralschema.Extensions{
   296  						XEmbeddedResource: true,
   297  					},
   298  					Properties: map[string]structuralschema.Structural{
   299  						"spec": {
   300  							Generic: structuralschema.Generic{Type: "object"},
   301  						},
   302  					},
   303  				},
   304  				"preserving": {
   305  					Generic: structuralschema.Generic{Type: "object"},
   306  					Extensions: structuralschema.Extensions{
   307  						XEmbeddedResource:      true,
   308  						XPreserveUnknownFields: true,
   309  					},
   310  				},
   311  				"nested": {
   312  					Generic: structuralschema.Generic{Type: "object"},
   313  					Extensions: structuralschema.Extensions{
   314  						XEmbeddedResource: true,
   315  					},
   316  					Properties: map[string]structuralschema.Structural{
   317  						"spec": {
   318  							Generic: structuralschema.Generic{Type: "object"},
   319  							Properties: map[string]structuralschema.Structural{
   320  								"embedded": {
   321  									Generic: structuralschema.Generic{Type: "object"},
   322  									Extensions: structuralschema.Extensions{
   323  										XEmbeddedResource: true,
   324  									},
   325  									Properties: map[string]structuralschema.Structural{
   326  										"spec": {
   327  											Generic: structuralschema.Generic{Type: "object"},
   328  										},
   329  									},
   330  								},
   331  							},
   332  						},
   333  					},
   334  				},
   335  			},
   336  		}, expectedObject: `
   337  {
   338    "pruned": {
   339      "apiVersion": "foo/v1",
   340      "kind": "Foo",
   341      "metadata": {
   342        "name": "instance",
   343        "unspecified": "bar"
   344      },
   345      "spec": {
   346      }
   347    },
   348    "preserving": {
   349      "apiVersion": "foo/v1",
   350      "kind": "Foo",
   351      "unspecified": "bar",
   352      "metadata": {
   353        "name": "instance",
   354        "unspecified": "bar"
   355      },
   356      "spec": {
   357        "unspecified": "bar"
   358      }
   359    },
   360    "nested": {
   361      "apiVersion": "foo/v1",
   362      "kind": "Foo",
   363      "metadata": {
   364        "name": "instance",
   365        "unspecified": "bar"
   366      },
   367      "spec": {
   368        "embedded": {
   369          "apiVersion": "foo/v1",
   370          "kind": "Foo",
   371          "metadata": {
   372            "name": "instance",
   373            "unspecified": "bar"
   374          },
   375          "spec": {
   376          }
   377        }
   378      }
   379    }
   380  }
   381  `, expectedPruned: []string{"nested.spec.embedded.spec.unspecified", "nested.spec.embedded.unspecified", "nested.spec.unspecified", "nested.unspecified", "pruned.spec.unspecified", "pruned.unspecified", "unspecified"}},
   382  		{name: "x-kubernetes-embedded-resource, with root=true", json: `
   383  {
   384    "apiVersion": "foo/v1",
   385    "kind": "Foo",
   386    "metadata": {
   387      "name": "instance",
   388      "namespace": "myns",
   389      "labels":{"foo":"bar"},
   390      "unspecified": "bar"
   391    },
   392    "unspecified":"bar",
   393    "pruned": {
   394      "apiVersion": "foo/v1",
   395      "kind": "Foo",
   396      "unspecified": "bar",
   397      "metadata": {
   398        "name": "instance",
   399        "namespace": "myns",
   400        "labels":{"foo":"bar"},
   401        "unspecified": "bar"
   402      },
   403      "spec": {
   404        "unspecified": "bar"
   405      }
   406    },
   407    "preserving": {
   408      "apiVersion": "foo/v1",
   409      "kind": "Foo",
   410      "unspecified": "bar",
   411      "metadata": {
   412        "name": "instance",
   413        "namespace": "myns",
   414        "labels":{"foo":"bar"},
   415        "unspecified": "bar"
   416      },
   417      "spec": {
   418        "unspecified": "bar"
   419      }
   420    },
   421    "nested": {
   422      "apiVersion": "foo/v1",
   423      "kind": "Foo",
   424      "unspecified": "bar",
   425      "metadata": {
   426        "name": "instance",
   427        "namespace": "myns",
   428        "labels":{"foo":"bar"},
   429        "unspecified": "bar"
   430      },
   431      "spec": {
   432        "unspecified": "bar",
   433        "embedded": {
   434          "apiVersion": "foo/v1",
   435          "kind": "Foo",
   436          "unspecified": "bar",
   437          "metadata": {
   438            "name": "instance",
   439            "namespace": "myns",
   440            "labels":{"foo":"bar"},
   441            "unspecified": "bar"
   442          },
   443          "spec": {
   444            "unspecified": "bar"
   445          }
   446        }
   447      }
   448    }
   449  }
   450  `, isResourceRoot: true, schema: &structuralschema.Structural{
   451  			Generic: structuralschema.Generic{Type: "object"},
   452  			Properties: map[string]structuralschema.Structural{
   453  				"metadata": {
   454  					Generic: structuralschema.Generic{Type: "object"},
   455  				},
   456  				"pruned": {
   457  					Generic: structuralschema.Generic{Type: "object"},
   458  					Extensions: structuralschema.Extensions{
   459  						XEmbeddedResource: true,
   460  					},
   461  					Properties: map[string]structuralschema.Structural{
   462  						"metadata": {
   463  							Generic: structuralschema.Generic{Type: "object"},
   464  						},
   465  						"spec": {
   466  							Generic: structuralschema.Generic{Type: "object"},
   467  						},
   468  					},
   469  				},
   470  				"preserving": {
   471  					Generic: structuralschema.Generic{Type: "object"},
   472  					Extensions: structuralschema.Extensions{
   473  						XEmbeddedResource:      true,
   474  						XPreserveUnknownFields: true,
   475  					},
   476  				},
   477  				"nested": {
   478  					Generic: structuralschema.Generic{Type: "object"},
   479  					Extensions: structuralschema.Extensions{
   480  						XEmbeddedResource: true,
   481  					},
   482  					Properties: map[string]structuralschema.Structural{
   483  						"spec": {
   484  							Generic: structuralschema.Generic{Type: "object"},
   485  							Properties: map[string]structuralschema.Structural{
   486  								"embedded": {
   487  									Generic: structuralschema.Generic{Type: "object"},
   488  									Extensions: structuralschema.Extensions{
   489  										XEmbeddedResource: true,
   490  									},
   491  									Properties: map[string]structuralschema.Structural{
   492  										"metadata": {
   493  											Generic: structuralschema.Generic{Type: "object"},
   494  										},
   495  										"spec": {
   496  											Generic: structuralschema.Generic{Type: "object"},
   497  										},
   498  									},
   499  								},
   500  							},
   501  						},
   502  					},
   503  				},
   504  			},
   505  		}, expectedObject: `
   506  {
   507    "apiVersion": "foo/v1",
   508    "kind": "Foo",
   509    "metadata": {
   510      "name": "instance",
   511      "namespace": "myns",
   512      "labels": {"foo": "bar"},
   513      "unspecified": "bar"
   514    },
   515    "pruned": {
   516      "apiVersion": "foo/v1",
   517      "kind": "Foo",
   518      "metadata": {
   519        "name": "instance",
   520        "namespace": "myns",
   521        "labels": {"foo": "bar"},
   522        "unspecified": "bar"
   523      },
   524      "spec": {
   525      }
   526    },
   527    "preserving": {
   528      "apiVersion": "foo/v1",
   529      "kind": "Foo",
   530      "unspecified": "bar",
   531      "metadata": {
   532        "name": "instance",
   533        "namespace": "myns",
   534        "labels": {"foo": "bar"},
   535        "unspecified": "bar"
   536      },
   537      "spec": {
   538        "unspecified": "bar"
   539      }
   540    },
   541    "nested": {
   542      "apiVersion": "foo/v1",
   543      "kind": "Foo",
   544      "metadata": {
   545        "name": "instance",
   546        "namespace": "myns",
   547        "labels": {"foo": "bar"},
   548        "unspecified": "bar"
   549      },
   550      "spec": {
   551        "embedded": {
   552          "apiVersion": "foo/v1",
   553          "kind": "Foo",
   554          "metadata": {
   555            "name": "instance",
   556            "namespace": "myns",
   557            "labels": {"foo": "bar"},
   558            "unspecified": "bar"
   559          },
   560          "spec": {
   561          }
   562        }
   563      }
   564    }
   565  }
   566  `, expectedPruned: []string{"nested.spec.embedded.spec.unspecified", "nested.spec.embedded.unspecified", "nested.spec.unspecified", "nested.unspecified", "pruned.spec.unspecified", "pruned.unspecified", "unspecified"}},
   567  	}
   568  	for _, tt := range tests {
   569  		t.Run(tt.name, func(t *testing.T) {
   570  			var in interface{}
   571  			if err := json.Unmarshal([]byte(tt.json), &in); err != nil {
   572  				t.Fatal(err)
   573  			}
   574  
   575  			var expectedObject interface{}
   576  			if err := json.Unmarshal([]byte(tt.expectedObject), &expectedObject); err != nil {
   577  				t.Fatal(err)
   578  			}
   579  
   580  			pruned := PruneWithOptions(in, tt.schema, tt.isResourceRoot, structuralschema.UnknownFieldPathOptions{
   581  				TrackUnknownFieldPaths: true,
   582  			})
   583  			if !reflect.DeepEqual(in, expectedObject) {
   584  				var buf bytes.Buffer
   585  				enc := json.NewEncoder(&buf)
   586  				enc.SetIndent("", "  ")
   587  				err := enc.Encode(in)
   588  				if err != nil {
   589  					t.Fatalf("unexpected result mashalling error: %v", err)
   590  				}
   591  				t.Errorf("expected object: %s\ngot: %s\ndiff: %s", tt.expectedObject, buf.String(), cmp.Diff(expectedObject, in))
   592  			}
   593  			if !reflect.DeepEqual(pruned, tt.expectedPruned) {
   594  				t.Errorf("expected pruned:\n\t%v\ngot:\n\t%v\n", strings.Join(tt.expectedPruned, "\n\t"), strings.Join(pruned, "\n\t"))
   595  			}
   596  
   597  			// now check that pruned is empty when TrackUnknownFieldPaths is false
   598  			emptyPruned := PruneWithOptions(in, tt.schema, tt.isResourceRoot, structuralschema.UnknownFieldPathOptions{})
   599  			if !reflect.DeepEqual(in, expectedObject) {
   600  				var buf bytes.Buffer
   601  				enc := json.NewEncoder(&buf)
   602  				enc.SetIndent("", "  ")
   603  				err := enc.Encode(in)
   604  				if err != nil {
   605  					t.Fatalf("unexpected result mashalling error: %v", err)
   606  				}
   607  				t.Errorf("expected object: %s\ngot: %s\ndiff: %s", tt.expectedObject, buf.String(), cmp.Diff(expectedObject, in))
   608  			}
   609  			if len(emptyPruned) > 0 {
   610  				t.Errorf("unexpectedly returned pruned fields: %v", emptyPruned)
   611  			}
   612  		})
   613  	}
   614  }
   615  
   616  const smallInstance = `
   617  {
   618    "unspecified":"bar",
   619    "alpha": "abc",
   620    "beta": 42.0,
   621    "unspecifiedObject": {"unspecified": "bar"},
   622    "pruning": {
   623       "pruning": {},
   624       "preserving": {"unspecified": "bar"}
   625    },
   626    "preserving": {
   627       "unspecified": "bar",
   628       "unspecifiedObject": {"unspecified": "bar"},
   629       "pruning": {},
   630       "preserving": {"unspecified": "bar"}
   631    }
   632  }
   633  `
   634  
   635  func BenchmarkPrune(b *testing.B) {
   636  	b.StopTimer()
   637  	b.ReportAllocs()
   638  
   639  	schema := &structuralschema.Structural{
   640  		Generic:    structuralschema.Generic{Type: "object"},
   641  		Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
   642  		Properties: map[string]structuralschema.Structural{
   643  			"alpha": {Generic: structuralschema.Generic{Type: "string"}},
   644  			"beta":  {Generic: structuralschema.Generic{Type: "number"}},
   645  			"pruning": {
   646  				Generic: structuralschema.Generic{Type: "object"},
   647  				Properties: map[string]structuralschema.Structural{
   648  					"preserving": {
   649  						Generic:    structuralschema.Generic{Type: "object"},
   650  						Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
   651  					},
   652  					"pruning": {
   653  						Generic: structuralschema.Generic{Type: "object"},
   654  					},
   655  				},
   656  			},
   657  			"preserving": {
   658  				Generic:    structuralschema.Generic{Type: "object"},
   659  				Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
   660  				Properties: map[string]structuralschema.Structural{
   661  					"preserving": {
   662  						Generic:    structuralschema.Generic{Type: "object"},
   663  						Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
   664  					},
   665  					"pruning": {
   666  						Generic: structuralschema.Generic{Type: "object"},
   667  					},
   668  				},
   669  			},
   670  		},
   671  	}
   672  
   673  	var obj map[string]interface{}
   674  	err := json.Unmarshal([]byte(smallInstance), &obj)
   675  	if err != nil {
   676  		b.Fatal(err)
   677  	}
   678  
   679  	instances := make([]map[string]interface{}, 0, b.N)
   680  	for i := 0; i < b.N; i++ {
   681  		instances = append(instances, runtime.DeepCopyJSON(obj))
   682  	}
   683  
   684  	b.StartTimer()
   685  	for i := 0; i < b.N; i++ {
   686  		Prune(instances[i], schema, true)
   687  	}
   688  }
   689  
   690  func BenchmarkDeepCopy(b *testing.B) {
   691  	b.StopTimer()
   692  	b.ReportAllocs()
   693  
   694  	var obj map[string]interface{}
   695  	err := json.Unmarshal([]byte(smallInstance), &obj)
   696  	if err != nil {
   697  		b.Fatal(err)
   698  	}
   699  
   700  	instances := make([]map[string]interface{}, 0, b.N)
   701  
   702  	b.StartTimer()
   703  	for i := 0; i < b.N; i++ {
   704  		//nolint:staticcheck //iccheck // SA4010 the result of append is never used, it's acceptable since in benchmark testing.
   705  		instances = append(instances, runtime.DeepCopyJSON(obj))
   706  	}
   707  }
   708  
   709  func BenchmarkUnmarshal(b *testing.B) {
   710  	b.StopTimer()
   711  	b.ReportAllocs()
   712  
   713  	instances := make([]map[string]interface{}, b.N)
   714  
   715  	b.StartTimer()
   716  	for i := 0; i < b.N; i++ {
   717  		err := json.Unmarshal([]byte(smallInstance), &instances[i])
   718  		if err != nil {
   719  			b.Fatal(err)
   720  		}
   721  	}
   722  }
   723  

View as plain text