k8s.io/apiserver@v0.31.1/pkg/cel/openapi/values_test.go (about)

     1  /*
     2  Copyright 2021 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 openapi
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  
    23  	"github.com/google/cel-go/common/types"
    24  	"github.com/google/cel-go/common/types/ref"
    25  	"github.com/google/cel-go/common/types/traits"
    26  
    27  	"k8s.io/kube-openapi/pkg/validation/spec"
    28  )
    29  
    30  var (
    31  	listTypeSet  = "set"
    32  	listTypeMap  = "map"
    33  	stringSchema = spec.StringProperty()
    34  	intSchema    = spec.Int64Property()
    35  
    36  	mapListElementSchema = &spec.Schema{
    37  		SchemaProps: spec.SchemaProps{
    38  			Type: []string{"object"},
    39  			Properties: map[string]spec.Schema{
    40  				"key": *stringSchema,
    41  				"val": *intSchema,
    42  			},
    43  		}}
    44  	mapListSchema = &spec.Schema{
    45  		SchemaProps: spec.SchemaProps{
    46  			Type:  []string{"array"},
    47  			Items: &spec.SchemaOrArray{Schema: mapListElementSchema},
    48  		},
    49  		VendorExtensible: spec.VendorExtensible{
    50  			Extensions: map[string]interface{}{
    51  				extListType:    listTypeMap,
    52  				extListMapKeys: []any{"key"},
    53  			}},
    54  	}
    55  	multiKeyMapListSchema = &spec.Schema{
    56  		SchemaProps: spec.SchemaProps{
    57  			Type: []string{"array"},
    58  			Items: &spec.SchemaOrArray{Schema: &spec.Schema{
    59  				SchemaProps: spec.SchemaProps{
    60  					Type: []string{"object"},
    61  					Properties: map[string]spec.Schema{
    62  						"key1": *stringSchema,
    63  						"key2": *stringSchema,
    64  						"val":  *intSchema,
    65  					},
    66  				}}}},
    67  		VendorExtensible: spec.VendorExtensible{
    68  			Extensions: map[string]interface{}{
    69  				extListType:    listTypeMap,
    70  				extListMapKeys: []any{"key1", "key2"},
    71  			}},
    72  	}
    73  	setListSchema = &spec.Schema{
    74  		SchemaProps: spec.SchemaProps{
    75  			Type:  []string{"array"},
    76  			Items: &spec.SchemaOrArray{Schema: stringSchema}},
    77  		VendorExtensible: spec.VendorExtensible{
    78  			Extensions: map[string]interface{}{
    79  				extListType: listTypeSet,
    80  			}},
    81  	}
    82  	atomicListSchema = &spec.Schema{
    83  		SchemaProps: spec.SchemaProps{
    84  			Type:  []string{"array"},
    85  			Items: &spec.SchemaOrArray{Schema: stringSchema},
    86  		}}
    87  	objectSchema = &spec.Schema{
    88  		SchemaProps: spec.SchemaProps{
    89  			Type: []string{"object"},
    90  			Properties: map[string]spec.Schema{
    91  				"field1": *stringSchema,
    92  				"field2": *stringSchema,
    93  			},
    94  		}}
    95  	mapSchema = &spec.Schema{
    96  		SchemaProps: spec.SchemaProps{
    97  			Type:                 []string{"object"},
    98  			AdditionalProperties: &spec.SchemaOrBool{Schema: stringSchema},
    99  		}}
   100  	emptyObjectSchema = &spec.Schema{
   101  		SchemaProps: spec.SchemaProps{Type: []string{"object"}},
   102  	}
   103  )
   104  
   105  func TestEquality(t *testing.T) {
   106  	cases := []struct {
   107  		name  string
   108  		lhs   ref.Val
   109  		rhs   ref.Val
   110  		equal bool
   111  	}{
   112  		{
   113  			name: "map lists are equal regardless of order",
   114  			lhs: UnstructuredToVal([]interface{}{
   115  				map[string]interface{}{
   116  					"key": "a",
   117  					"val": 1,
   118  				},
   119  				map[string]interface{}{
   120  					"key": "b",
   121  					"val": 2,
   122  				},
   123  			}, mapListSchema),
   124  			rhs: UnstructuredToVal([]interface{}{
   125  				map[string]interface{}{
   126  					"key": "b",
   127  					"val": 2,
   128  				},
   129  				map[string]interface{}{
   130  					"key": "a",
   131  					"val": 1,
   132  				},
   133  			}, mapListSchema),
   134  			equal: true,
   135  		},
   136  		{
   137  			name: "map lists are not equal if contents differs",
   138  			lhs: UnstructuredToVal([]interface{}{
   139  				map[string]interface{}{
   140  					"key": "a",
   141  					"val": 1,
   142  				},
   143  				map[string]interface{}{
   144  					"key": "b",
   145  					"val": 2,
   146  				},
   147  			}, mapListSchema),
   148  			rhs: UnstructuredToVal([]interface{}{
   149  				map[string]interface{}{
   150  					"key": "a",
   151  					"val": 1,
   152  				},
   153  				map[string]interface{}{
   154  					"key": "b",
   155  					"val": 3,
   156  				},
   157  			}, mapListSchema),
   158  			equal: false,
   159  		},
   160  		{
   161  			name: "map lists are not equal if length differs",
   162  			lhs: UnstructuredToVal([]interface{}{
   163  				map[string]interface{}{
   164  					"key": "a",
   165  					"val": 1,
   166  				},
   167  				map[string]interface{}{
   168  					"key": "b",
   169  					"val": 2,
   170  				},
   171  			}, mapListSchema),
   172  			rhs: UnstructuredToVal([]interface{}{
   173  				map[string]interface{}{
   174  					"key": "a",
   175  					"val": 1,
   176  				},
   177  				map[string]interface{}{
   178  					"key": "b",
   179  					"val": 2,
   180  				},
   181  				map[string]interface{}{
   182  					"key": "c",
   183  					"val": 3,
   184  				},
   185  			}, mapListSchema),
   186  			equal: false,
   187  		},
   188  		{
   189  			name: "multi-key map lists are equal regardless of order",
   190  			lhs: UnstructuredToVal([]interface{}{
   191  				map[string]interface{}{
   192  					"key1": "a1",
   193  					"key2": "a2",
   194  					"val":  1,
   195  				},
   196  				map[string]interface{}{
   197  					"key1": "b1",
   198  					"key2": "b2",
   199  					"val":  2,
   200  				},
   201  			}, multiKeyMapListSchema),
   202  			rhs: UnstructuredToVal([]interface{}{
   203  				map[string]interface{}{
   204  					"key1": "b1",
   205  					"key2": "b2",
   206  					"val":  2,
   207  				},
   208  				map[string]interface{}{
   209  					"key1": "a1",
   210  					"key2": "a2",
   211  					"val":  1,
   212  				},
   213  			}, multiKeyMapListSchema),
   214  			equal: true,
   215  		},
   216  		{
   217  			name: "multi-key map lists with different contents are not equal",
   218  			lhs: UnstructuredToVal([]interface{}{
   219  				map[string]interface{}{
   220  					"key1": "a1",
   221  					"key2": "a2",
   222  					"val":  1,
   223  				},
   224  				map[string]interface{}{
   225  					"key1": "b1",
   226  					"key2": "b2",
   227  					"val":  2,
   228  				},
   229  			}, multiKeyMapListSchema),
   230  			rhs: UnstructuredToVal([]interface{}{
   231  				map[string]interface{}{
   232  					"key1": "a1",
   233  					"key2": "a2",
   234  					"val":  1,
   235  				},
   236  				map[string]interface{}{
   237  					"key1": "b1",
   238  					"key2": "b2",
   239  					"val":  3,
   240  				},
   241  			}, multiKeyMapListSchema),
   242  			equal: false,
   243  		},
   244  		{
   245  			name: "multi-key map lists with different keys are not equal",
   246  			lhs: UnstructuredToVal([]interface{}{
   247  				map[string]interface{}{
   248  					"key1": "a1",
   249  					"key2": "a2",
   250  					"val":  1,
   251  				},
   252  				map[string]interface{}{
   253  					"key1": "b1",
   254  					"key2": "b2",
   255  					"val":  2,
   256  				},
   257  			}, multiKeyMapListSchema),
   258  			rhs: UnstructuredToVal([]interface{}{
   259  				map[string]interface{}{
   260  					"key1": "a1",
   261  					"key2": "a2",
   262  					"val":  1,
   263  				},
   264  				map[string]interface{}{
   265  					"key1": "c1",
   266  					"key2": "c2",
   267  					"val":  3,
   268  				},
   269  			}, multiKeyMapListSchema),
   270  			equal: false,
   271  		},
   272  		{
   273  			name: "multi-key map lists with different lengths are not equal",
   274  			lhs: UnstructuredToVal([]interface{}{
   275  				map[string]interface{}{
   276  					"key1": "a1",
   277  					"key2": "a2",
   278  					"val":  1,
   279  				},
   280  			}, multiKeyMapListSchema),
   281  			rhs: UnstructuredToVal([]interface{}{
   282  				map[string]interface{}{
   283  					"key1": "a1",
   284  					"key2": "a2",
   285  					"val":  1,
   286  				},
   287  				map[string]interface{}{
   288  					"key1": "b1",
   289  					"key2": "b2",
   290  					"val":  3,
   291  				},
   292  			}, multiKeyMapListSchema),
   293  			equal: false,
   294  		},
   295  		{
   296  			name:  "set lists are equal regardless of order",
   297  			lhs:   UnstructuredToVal([]interface{}{"a", "b"}, setListSchema),
   298  			rhs:   UnstructuredToVal([]interface{}{"b", "a"}, setListSchema),
   299  			equal: true,
   300  		},
   301  		{
   302  			name:  "set lists are not equal if contents differ",
   303  			lhs:   UnstructuredToVal([]interface{}{"a", "b"}, setListSchema),
   304  			rhs:   UnstructuredToVal([]interface{}{"a", "c"}, setListSchema),
   305  			equal: false,
   306  		},
   307  		{
   308  			name:  "set lists are not equal if lengths differ",
   309  			lhs:   UnstructuredToVal([]interface{}{"a", "b"}, setListSchema),
   310  			rhs:   UnstructuredToVal([]interface{}{"a", "b", "c"}, setListSchema),
   311  			equal: false,
   312  		},
   313  		{
   314  			name:  "identical atomic lists are equal",
   315  			lhs:   UnstructuredToVal([]interface{}{"a", "b"}, atomicListSchema),
   316  			rhs:   UnstructuredToVal([]interface{}{"a", "b"}, atomicListSchema),
   317  			equal: true,
   318  		},
   319  		{
   320  			name:  "atomic lists are not equal if order differs",
   321  			lhs:   UnstructuredToVal([]interface{}{"a", "b"}, atomicListSchema),
   322  			rhs:   UnstructuredToVal([]interface{}{"b", "a"}, atomicListSchema),
   323  			equal: false,
   324  		},
   325  		{
   326  			name:  "atomic lists are not equal if contents differ",
   327  			lhs:   UnstructuredToVal([]interface{}{"a", "b"}, atomicListSchema),
   328  			rhs:   UnstructuredToVal([]interface{}{"a", "c"}, atomicListSchema),
   329  			equal: false,
   330  		},
   331  		{
   332  			name:  "atomic lists are not equal if lengths differ",
   333  			lhs:   UnstructuredToVal([]interface{}{"a", "b"}, atomicListSchema),
   334  			rhs:   UnstructuredToVal([]interface{}{"a", "b", "c"}, atomicListSchema),
   335  			equal: false,
   336  		},
   337  		{
   338  			name:  "empty objects are equal",
   339  			lhs:   UnstructuredToVal(map[string]interface{}{}, emptyObjectSchema),
   340  			rhs:   UnstructuredToVal(map[string]interface{}{}, emptyObjectSchema),
   341  			equal: true,
   342  		},
   343  		{
   344  			name:  "identical objects are equal",
   345  			lhs:   UnstructuredToVal(map[string]interface{}{"field1": "a", "field2": "b"}, objectSchema),
   346  			rhs:   UnstructuredToVal(map[string]interface{}{"field1": "a", "field2": "b"}, objectSchema),
   347  			equal: true,
   348  		},
   349  		{
   350  			name:  "objects are equal regardless of field order",
   351  			lhs:   UnstructuredToVal(map[string]interface{}{"field1": "a", "field2": "b"}, objectSchema),
   352  			rhs:   UnstructuredToVal(map[string]interface{}{"field2": "b", "field1": "a"}, objectSchema),
   353  			equal: true,
   354  		},
   355  		{
   356  			name:  "objects are not equal if contents differs",
   357  			lhs:   UnstructuredToVal(map[string]interface{}{"field1": "a", "field2": "b"}, objectSchema),
   358  			rhs:   UnstructuredToVal(map[string]interface{}{"field1": "a", "field2": "c"}, objectSchema),
   359  			equal: false,
   360  		},
   361  		{
   362  			name:  "objects are not equal if length differs",
   363  			lhs:   UnstructuredToVal(map[string]interface{}{"field1": "a", "field2": "b"}, objectSchema),
   364  			rhs:   UnstructuredToVal(map[string]interface{}{"field1": "a"}, objectSchema),
   365  			equal: false,
   366  		},
   367  		{
   368  			name:  "identical maps are equal",
   369  			lhs:   UnstructuredToVal(map[string]interface{}{"key1": "a", "key2": "b"}, mapSchema),
   370  			rhs:   UnstructuredToVal(map[string]interface{}{"key1": "a", "key2": "b"}, mapSchema),
   371  			equal: true,
   372  		},
   373  		{
   374  			name:  "maps are equal regardless of field order",
   375  			lhs:   UnstructuredToVal(map[string]interface{}{"key1": "a", "key2": "b"}, mapSchema),
   376  			rhs:   UnstructuredToVal(map[string]interface{}{"key2": "b", "key1": "a"}, mapSchema),
   377  			equal: true,
   378  		},
   379  		{
   380  			name:  "maps are not equal if contents differs",
   381  			lhs:   UnstructuredToVal(map[string]interface{}{"key1": "a", "key2": "b"}, mapSchema),
   382  			rhs:   UnstructuredToVal(map[string]interface{}{"key1": "a", "key2": "c"}, mapSchema),
   383  			equal: false,
   384  		},
   385  		{
   386  			name:  "maps are not equal if length differs",
   387  			lhs:   UnstructuredToVal(map[string]interface{}{"key1": "a", "key2": "b"}, mapSchema),
   388  			rhs:   UnstructuredToVal(map[string]interface{}{"key1": "a", "key2": "b", "key3": "c"}, mapSchema),
   389  			equal: false,
   390  		},
   391  	}
   392  
   393  	for _, tc := range cases {
   394  		t.Run(tc.name, func(t *testing.T) {
   395  			// Compare types with schema against themselves
   396  			if tc.lhs.Equal(tc.rhs) != types.Bool(tc.equal) {
   397  				t.Errorf("expected Equals to return %v", tc.equal)
   398  			}
   399  			if tc.rhs.Equal(tc.lhs) != types.Bool(tc.equal) {
   400  				t.Errorf("expected Equals to return %v", tc.equal)
   401  			}
   402  
   403  			// Compare types with schema against native types. This is slightly different than how
   404  			// CEL performs equality against data literals, but is a good sanity check.
   405  			if tc.lhs.Equal(types.DefaultTypeAdapter.NativeToValue(tc.rhs.Value())) != types.Bool(tc.equal) {
   406  				t.Errorf("expected unstructuredVal.Equals(<native type>) to return %v", tc.equal)
   407  			}
   408  			if tc.rhs.Equal(types.DefaultTypeAdapter.NativeToValue(tc.lhs.Value())) != types.Bool(tc.equal) {
   409  				t.Errorf("expected unstructuredVal.Equals(<native type>) to return %v", tc.equal)
   410  			}
   411  		})
   412  	}
   413  }
   414  
   415  func TestLister(t *testing.T) {
   416  	cases := []struct {
   417  		name         string
   418  		unstructured []interface{}
   419  		schema       *spec.Schema
   420  		itemSchema   *spec.Schema
   421  		size         int64
   422  		notContains  []ref.Val
   423  		addition     []interface{}
   424  		expectAdded  []interface{}
   425  	}{
   426  		{
   427  			name: "map list",
   428  			unstructured: []interface{}{
   429  				map[string]interface{}{
   430  					"key": "a",
   431  					"val": 1,
   432  				},
   433  				map[string]interface{}{
   434  					"key": "b",
   435  					"val": 2,
   436  				},
   437  			},
   438  			schema:     mapListSchema,
   439  			itemSchema: mapListElementSchema,
   440  			size:       2,
   441  			notContains: []ref.Val{
   442  				UnstructuredToVal(map[string]interface{}{
   443  					"key": "a",
   444  					"val": 2,
   445  				}, mapListElementSchema),
   446  				UnstructuredToVal(map[string]interface{}{
   447  					"key": "c",
   448  					"val": 1,
   449  				}, mapListElementSchema),
   450  			},
   451  			addition: []interface{}{
   452  				map[string]interface{}{
   453  					"key": "b",
   454  					"val": 3,
   455  				},
   456  				map[string]interface{}{
   457  					"key": "c",
   458  					"val": 4,
   459  				},
   460  			},
   461  			expectAdded: []interface{}{
   462  				map[string]interface{}{
   463  					"key": "a",
   464  					"val": 1,
   465  				},
   466  				map[string]interface{}{
   467  					"key": "b",
   468  					"val": 3,
   469  				},
   470  				map[string]interface{}{
   471  					"key": "c",
   472  					"val": 4,
   473  				},
   474  			},
   475  		},
   476  		{
   477  			name:         "set list",
   478  			unstructured: []interface{}{"a", "b"},
   479  			schema:       setListSchema,
   480  			itemSchema:   stringSchema,
   481  			size:         2,
   482  			notContains:  []ref.Val{UnstructuredToVal("c", stringSchema)},
   483  			addition:     []interface{}{"b", "c"},
   484  			expectAdded:  []interface{}{"a", "b", "c"},
   485  		},
   486  		{
   487  			name:         "atomic list",
   488  			unstructured: []interface{}{"a", "b"},
   489  			schema:       atomicListSchema,
   490  			itemSchema:   stringSchema,
   491  			size:         2,
   492  			notContains:  []ref.Val{UnstructuredToVal("c", stringSchema)},
   493  			addition:     []interface{}{"b", "c"},
   494  			expectAdded:  []interface{}{"a", "b", "b", "c"},
   495  		},
   496  	}
   497  
   498  	for _, tc := range cases {
   499  		t.Run(tc.name, func(t *testing.T) {
   500  			lister := UnstructuredToVal(tc.unstructured, tc.schema).(traits.Lister)
   501  			if lister.Size().Value() != tc.size {
   502  				t.Errorf("Expected Size to return %d but got %d", tc.size, lister.Size().Value())
   503  			}
   504  			iter := lister.Iterator()
   505  			for i := 0; i < int(tc.size); i++ {
   506  				get := lister.Get(types.Int(i)).Value()
   507  				if !reflect.DeepEqual(get, tc.unstructured[i]) {
   508  					t.Errorf("Expected Get to return %v for index %d but got %v", tc.unstructured[i], i, get)
   509  				}
   510  				if iter.HasNext() != types.True {
   511  					t.Error("Expected HasNext to return true")
   512  				}
   513  				next := iter.Next().Value()
   514  				if !reflect.DeepEqual(next, tc.unstructured[i]) {
   515  					t.Errorf("Expected Next to return %v for index %d but got %v", tc.unstructured[i], i, next)
   516  				}
   517  			}
   518  			if iter.HasNext() != types.False {
   519  				t.Error("Expected HasNext to return false")
   520  			}
   521  			for _, contains := range tc.unstructured {
   522  				if lister.Contains(UnstructuredToVal(contains, tc.itemSchema)) != types.True {
   523  					t.Errorf("Expected Contains to return true for %v", contains)
   524  				}
   525  			}
   526  			for _, notContains := range tc.notContains {
   527  				if lister.Contains(notContains) != types.False {
   528  					t.Errorf("Expected Contains to return false for %v", notContains)
   529  				}
   530  			}
   531  
   532  			addition := UnstructuredToVal(tc.addition, tc.schema).(traits.Lister)
   533  			added := lister.Add(addition).Value()
   534  			if !reflect.DeepEqual(added, tc.expectAdded) {
   535  				t.Errorf("Expected Add to return %v but got %v", tc.expectAdded, added)
   536  			}
   537  		})
   538  	}
   539  }
   540  
   541  func TestMapper(t *testing.T) {
   542  	cases := []struct {
   543  		name           string
   544  		unstructured   map[string]interface{}
   545  		schema         *spec.Schema
   546  		propertySchema func(key string) (*spec.Schema, bool)
   547  		size           int64
   548  		notContains    []ref.Val
   549  	}{
   550  		{
   551  			name: "object",
   552  			unstructured: map[string]interface{}{
   553  				"field1": "a",
   554  				"field2": "b",
   555  			},
   556  			schema: objectSchema,
   557  			propertySchema: func(key string) (*spec.Schema, bool) {
   558  				if s, ok := objectSchema.Properties[key]; ok {
   559  					return &s, true
   560  				}
   561  				return nil, false
   562  			},
   563  			size: 2,
   564  			notContains: []ref.Val{
   565  				UnstructuredToVal("field3", stringSchema),
   566  			},
   567  		},
   568  		{
   569  			name: "map",
   570  			unstructured: map[string]interface{}{
   571  				"key1": "a",
   572  				"key2": "b",
   573  			},
   574  			schema:         mapSchema,
   575  			propertySchema: func(key string) (*spec.Schema, bool) { return mapSchema.AdditionalProperties.Schema, true },
   576  			size:           2,
   577  			notContains: []ref.Val{
   578  				UnstructuredToVal("key3", stringSchema),
   579  			},
   580  		},
   581  	}
   582  
   583  	for _, tc := range cases {
   584  		t.Run(tc.name, func(t *testing.T) {
   585  			mapper := UnstructuredToVal(tc.unstructured, tc.schema).(traits.Mapper)
   586  			if mapper.Size().Value() != tc.size {
   587  				t.Errorf("Expected Size to return %d but got %d", tc.size, mapper.Size().Value())
   588  			}
   589  			iter := mapper.Iterator()
   590  			iterResults := map[interface{}]struct{}{}
   591  			keys := map[interface{}]struct{}{}
   592  			for k := range tc.unstructured {
   593  				keys[k] = struct{}{}
   594  				get := mapper.Get(types.String(k)).Value()
   595  				if !reflect.DeepEqual(get, tc.unstructured[k]) {
   596  					t.Errorf("Expected Get to return %v for key %s but got %v", tc.unstructured[k], k, get)
   597  				}
   598  				if iter.HasNext() != types.True {
   599  					t.Error("Expected HasNext to return true")
   600  				}
   601  				iterResults[iter.Next().Value()] = struct{}{}
   602  			}
   603  			if !reflect.DeepEqual(iterResults, keys) {
   604  				t.Errorf("Expected accumulation of iterator.Next calls to be %v but got %v", keys, iterResults)
   605  			}
   606  			if iter.HasNext() != types.False {
   607  				t.Error("Expected HasNext to return false")
   608  			}
   609  			for contains := range tc.unstructured {
   610  				if mapper.Contains(UnstructuredToVal(contains, stringSchema)) != types.True {
   611  					t.Errorf("Expected Contains to return true for %v", contains)
   612  				}
   613  			}
   614  			for _, notContains := range tc.notContains {
   615  				if mapper.Contains(notContains) != types.False {
   616  					t.Errorf("Expected Contains to return false for %v", notContains)
   617  				}
   618  			}
   619  		})
   620  	}
   621  }
   622  
   623  func BenchmarkUnstructuredToVal(b *testing.B) {
   624  	u := []interface{}{
   625  		map[string]interface{}{
   626  			"key": "a",
   627  			"val": 1,
   628  		},
   629  		map[string]interface{}{
   630  			"key": "b",
   631  			"val": 2,
   632  		},
   633  		map[string]interface{}{
   634  			"key": "@b",
   635  			"val": 2,
   636  		},
   637  	}
   638  
   639  	b.ReportAllocs()
   640  	b.ResetTimer()
   641  
   642  	for n := 0; n < b.N; n++ {
   643  		if val := UnstructuredToVal(u, mapListSchema); val == nil {
   644  			b.Fatal(val)
   645  		}
   646  	}
   647  }
   648  
   649  func BenchmarkUnstructuredToValWithEscape(b *testing.B) {
   650  	u := []interface{}{
   651  		map[string]interface{}{
   652  			"key": "a.1",
   653  			"val": "__i.1",
   654  		},
   655  		map[string]interface{}{
   656  			"key": "b.1",
   657  			"val": 2,
   658  		},
   659  	}
   660  
   661  	b.ReportAllocs()
   662  	b.ResetTimer()
   663  
   664  	for n := 0; n < b.N; n++ {
   665  		if val := UnstructuredToVal(u, mapListSchema); val == nil {
   666  			b.Fatal(val)
   667  		}
   668  	}
   669  }