...

Source file src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/values_test.go

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

     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 cel
    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/apiextensions-apiserver/pkg/apiserver/schema"
    28  )
    29  
    30  var (
    31  	listTypeSet  = "set"
    32  	listTypeMap  = "map"
    33  	stringSchema = schema.Structural{
    34  		Generic: schema.Generic{
    35  			Type: "string",
    36  		},
    37  	}
    38  	intSchema = schema.Structural{
    39  		Generic: schema.Generic{
    40  			Type: "integer",
    41  		},
    42  		ValueValidation: &schema.ValueValidation{
    43  			Format: "int64",
    44  		},
    45  	}
    46  	mapListElementSchema = schema.Structural{
    47  		Generic: schema.Generic{
    48  			Type: "object",
    49  		},
    50  		Properties: map[string]schema.Structural{
    51  			"key": stringSchema,
    52  			"val": intSchema,
    53  		},
    54  	}
    55  	mapListSchema = schema.Structural{
    56  		Extensions: schema.Extensions{XListType: &listTypeMap, XListMapKeys: []string{"key"}},
    57  		Generic: schema.Generic{
    58  			Type: "array",
    59  		},
    60  		Items: &mapListElementSchema,
    61  	}
    62  	multiKeyMapListSchema = schema.Structural{
    63  		Extensions: schema.Extensions{XListType: &listTypeMap, XListMapKeys: []string{"key1", "key2"}},
    64  		Generic: schema.Generic{
    65  			Type: "array",
    66  		},
    67  		Items: &schema.Structural{
    68  			Generic: schema.Generic{
    69  				Type: "object",
    70  			},
    71  			Properties: map[string]schema.Structural{
    72  				"key1": stringSchema,
    73  				"key2": stringSchema,
    74  				"val":  intSchema,
    75  			},
    76  		},
    77  	}
    78  	setListSchema = schema.Structural{
    79  		Extensions: schema.Extensions{XListType: &listTypeSet},
    80  		Generic: schema.Generic{
    81  			Type: "array",
    82  		},
    83  		Items: &stringSchema,
    84  	}
    85  	atomicListSchema = schema.Structural{
    86  		Generic: schema.Generic{
    87  			Type: "array",
    88  		},
    89  		Items: &stringSchema,
    90  	}
    91  	objectSchema = schema.Structural{
    92  		Generic: schema.Generic{
    93  			Type: "object",
    94  		},
    95  		Properties: map[string]schema.Structural{
    96  			"field1": stringSchema,
    97  			"field2": stringSchema,
    98  		},
    99  	}
   100  	mapSchema = schema.Structural{
   101  		Generic: schema.Generic{
   102  			Type: "object",
   103  			AdditionalProperties: &schema.StructuralOrBool{
   104  				Bool:       true,
   105  				Structural: &stringSchema,
   106  			},
   107  		},
   108  	}
   109  )
   110  
   111  func TestEquality(t *testing.T) {
   112  	cases := []struct {
   113  		name  string
   114  		lhs   ref.Val
   115  		rhs   ref.Val
   116  		equal bool
   117  	}{
   118  		{
   119  			name: "map lists are equal regardless of order",
   120  			lhs: UnstructuredToVal([]interface{}{
   121  				map[string]interface{}{
   122  					"key": "a",
   123  					"val": 1,
   124  				},
   125  				map[string]interface{}{
   126  					"key": "b",
   127  					"val": 2,
   128  				},
   129  			}, &mapListSchema),
   130  			rhs: UnstructuredToVal([]interface{}{
   131  				map[string]interface{}{
   132  					"key": "b",
   133  					"val": 2,
   134  				},
   135  				map[string]interface{}{
   136  					"key": "a",
   137  					"val": 1,
   138  				},
   139  			}, &mapListSchema),
   140  			equal: true,
   141  		},
   142  		{
   143  			name: "map lists are not equal if contents differs",
   144  			lhs: UnstructuredToVal([]interface{}{
   145  				map[string]interface{}{
   146  					"key": "a",
   147  					"val": 1,
   148  				},
   149  				map[string]interface{}{
   150  					"key": "b",
   151  					"val": 2,
   152  				},
   153  			}, &mapListSchema),
   154  			rhs: UnstructuredToVal([]interface{}{
   155  				map[string]interface{}{
   156  					"key": "a",
   157  					"val": 1,
   158  				},
   159  				map[string]interface{}{
   160  					"key": "b",
   161  					"val": 3,
   162  				},
   163  			}, &mapListSchema),
   164  			equal: false,
   165  		},
   166  		{
   167  			name: "map lists are not equal if length differs",
   168  			lhs: UnstructuredToVal([]interface{}{
   169  				map[string]interface{}{
   170  					"key": "a",
   171  					"val": 1,
   172  				},
   173  				map[string]interface{}{
   174  					"key": "b",
   175  					"val": 2,
   176  				},
   177  			}, &mapListSchema),
   178  			rhs: UnstructuredToVal([]interface{}{
   179  				map[string]interface{}{
   180  					"key": "a",
   181  					"val": 1,
   182  				},
   183  				map[string]interface{}{
   184  					"key": "b",
   185  					"val": 2,
   186  				},
   187  				map[string]interface{}{
   188  					"key": "c",
   189  					"val": 3,
   190  				},
   191  			}, &mapListSchema),
   192  			equal: false,
   193  		},
   194  		{
   195  			name: "multi-key map lists are equal regardless of order",
   196  			lhs: UnstructuredToVal([]interface{}{
   197  				map[string]interface{}{
   198  					"key1": "a1",
   199  					"key2": "a2",
   200  					"val":  1,
   201  				},
   202  				map[string]interface{}{
   203  					"key1": "b1",
   204  					"key2": "b2",
   205  					"val":  2,
   206  				},
   207  			}, &multiKeyMapListSchema),
   208  			rhs: UnstructuredToVal([]interface{}{
   209  				map[string]interface{}{
   210  					"key1": "b1",
   211  					"key2": "b2",
   212  					"val":  2,
   213  				},
   214  				map[string]interface{}{
   215  					"key1": "a1",
   216  					"key2": "a2",
   217  					"val":  1,
   218  				},
   219  			}, &multiKeyMapListSchema),
   220  			equal: true,
   221  		},
   222  		{
   223  			name: "multi-key map lists with different contents are not equal",
   224  			lhs: UnstructuredToVal([]interface{}{
   225  				map[string]interface{}{
   226  					"key1": "a1",
   227  					"key2": "a2",
   228  					"val":  1,
   229  				},
   230  				map[string]interface{}{
   231  					"key1": "b1",
   232  					"key2": "b2",
   233  					"val":  2,
   234  				},
   235  			}, &multiKeyMapListSchema),
   236  			rhs: UnstructuredToVal([]interface{}{
   237  				map[string]interface{}{
   238  					"key1": "a1",
   239  					"key2": "a2",
   240  					"val":  1,
   241  				},
   242  				map[string]interface{}{
   243  					"key1": "b1",
   244  					"key2": "b2",
   245  					"val":  3,
   246  				},
   247  			}, &multiKeyMapListSchema),
   248  			equal: false,
   249  		},
   250  		{
   251  			name: "multi-key map lists with different keys are not equal",
   252  			lhs: UnstructuredToVal([]interface{}{
   253  				map[string]interface{}{
   254  					"key1": "a1",
   255  					"key2": "a2",
   256  					"val":  1,
   257  				},
   258  				map[string]interface{}{
   259  					"key1": "b1",
   260  					"key2": "b2",
   261  					"val":  2,
   262  				},
   263  			}, &multiKeyMapListSchema),
   264  			rhs: UnstructuredToVal([]interface{}{
   265  				map[string]interface{}{
   266  					"key1": "a1",
   267  					"key2": "a2",
   268  					"val":  1,
   269  				},
   270  				map[string]interface{}{
   271  					"key1": "c1",
   272  					"key2": "c2",
   273  					"val":  3,
   274  				},
   275  			}, &multiKeyMapListSchema),
   276  			equal: false,
   277  		},
   278  		{
   279  			name: "multi-key map lists with different lengths are not equal",
   280  			lhs: UnstructuredToVal([]interface{}{
   281  				map[string]interface{}{
   282  					"key1": "a1",
   283  					"key2": "a2",
   284  					"val":  1,
   285  				},
   286  			}, &multiKeyMapListSchema),
   287  			rhs: UnstructuredToVal([]interface{}{
   288  				map[string]interface{}{
   289  					"key1": "a1",
   290  					"key2": "a2",
   291  					"val":  1,
   292  				},
   293  				map[string]interface{}{
   294  					"key1": "b1",
   295  					"key2": "b2",
   296  					"val":  3,
   297  				},
   298  			}, &multiKeyMapListSchema),
   299  			equal: false,
   300  		},
   301  		{
   302  			name:  "set lists are equal regardless of order",
   303  			lhs:   UnstructuredToVal([]interface{}{"a", "b"}, &setListSchema),
   304  			rhs:   UnstructuredToVal([]interface{}{"b", "a"}, &setListSchema),
   305  			equal: true,
   306  		},
   307  		{
   308  			name:  "set lists are not equal if contents differ",
   309  			lhs:   UnstructuredToVal([]interface{}{"a", "b"}, &setListSchema),
   310  			rhs:   UnstructuredToVal([]interface{}{"a", "c"}, &setListSchema),
   311  			equal: false,
   312  		},
   313  		{
   314  			name:  "set lists are not equal if lengths differ",
   315  			lhs:   UnstructuredToVal([]interface{}{"a", "b"}, &setListSchema),
   316  			rhs:   UnstructuredToVal([]interface{}{"a", "b", "c"}, &setListSchema),
   317  			equal: false,
   318  		},
   319  		{
   320  			name:  "identical atomic lists are equal",
   321  			lhs:   UnstructuredToVal([]interface{}{"a", "b"}, &atomicListSchema),
   322  			rhs:   UnstructuredToVal([]interface{}{"a", "b"}, &atomicListSchema),
   323  			equal: true,
   324  		},
   325  		{
   326  			name:  "atomic lists are not equal if order differs",
   327  			lhs:   UnstructuredToVal([]interface{}{"a", "b"}, &atomicListSchema),
   328  			rhs:   UnstructuredToVal([]interface{}{"b", "a"}, &atomicListSchema),
   329  			equal: false,
   330  		},
   331  		{
   332  			name:  "atomic lists are not equal if contents differ",
   333  			lhs:   UnstructuredToVal([]interface{}{"a", "b"}, &atomicListSchema),
   334  			rhs:   UnstructuredToVal([]interface{}{"a", "c"}, &atomicListSchema),
   335  			equal: false,
   336  		},
   337  		{
   338  			name:  "atomic lists are not equal if lengths differ",
   339  			lhs:   UnstructuredToVal([]interface{}{"a", "b"}, &atomicListSchema),
   340  			rhs:   UnstructuredToVal([]interface{}{"a", "b", "c"}, &atomicListSchema),
   341  			equal: false,
   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       *schema.Structural
   420  		itemSchema   *schema.Structural
   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         *schema.Structural
   546  		propertySchema func(key string) (*schema.Structural, 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) (*schema.Structural, 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) (*schema.Structural, bool) { return mapSchema.AdditionalProperties.Structural, 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  }
   670  

View as plain text