k8s.io/kubernetes@v1.29.3/pkg/scheduler/framework/runtime/registry_test.go (about)

     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 runtime
    18  
    19  import (
    20  	"context"
    21  	"reflect"
    22  	"testing"
    23  
    24  	"github.com/google/uuid"
    25  
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  	"k8s.io/kubernetes/pkg/scheduler/framework"
    28  )
    29  
    30  func TestDecodeInto(t *testing.T) {
    31  	type PluginFooConfig struct {
    32  		FooTest string `json:"fooTest,omitempty"`
    33  	}
    34  	tests := []struct {
    35  		name     string
    36  		args     *runtime.Unknown
    37  		expected PluginFooConfig
    38  	}{
    39  		{
    40  			name: "test decode for JSON config",
    41  			args: &runtime.Unknown{
    42  				ContentType: runtime.ContentTypeJSON,
    43  				Raw: []byte(`{
    44  					"fooTest": "test decode"
    45  				}`),
    46  			},
    47  			expected: PluginFooConfig{
    48  				FooTest: "test decode",
    49  			},
    50  		},
    51  		{
    52  			name: "test decode for YAML config",
    53  			args: &runtime.Unknown{
    54  				ContentType: runtime.ContentTypeYAML,
    55  				Raw:         []byte(`fooTest: "test decode"`),
    56  			},
    57  			expected: PluginFooConfig{
    58  				FooTest: "test decode",
    59  			},
    60  		},
    61  	}
    62  
    63  	for _, test := range tests {
    64  		t.Run(test.name, func(t *testing.T) {
    65  			var pluginFooConf PluginFooConfig
    66  			if err := DecodeInto(test.args, &pluginFooConf); err != nil {
    67  				t.Errorf("DecodeInto(): failed to decode args %+v: %v", test.args, err)
    68  			}
    69  			if !reflect.DeepEqual(test.expected, pluginFooConf) {
    70  				t.Errorf("DecodeInto(): failed to decode plugin config, expected: %+v, got: %+v",
    71  					test.expected, pluginFooConf)
    72  			}
    73  		})
    74  	}
    75  }
    76  
    77  // isRegistryEqual compares two registries for equality. This function is used in place of
    78  // reflect.DeepEqual() and cmp() as they don't compare function values.
    79  func isRegistryEqual(registryX, registryY Registry) bool {
    80  	for name, pluginFactory := range registryY {
    81  		if val, ok := registryX[name]; ok {
    82  			p1, _ := pluginFactory(nil, nil, nil)
    83  			p2, _ := val(nil, nil, nil)
    84  			if p1.Name() != p2.Name() {
    85  				// pluginFactory functions are not the same.
    86  				return false
    87  			}
    88  		} else {
    89  			// registryY contains an entry that is not present in registryX
    90  			return false
    91  		}
    92  	}
    93  
    94  	for name := range registryX {
    95  		if _, ok := registryY[name]; !ok {
    96  			// registryX contains an entry that is not present in registryY
    97  			return false
    98  		}
    99  	}
   100  
   101  	return true
   102  }
   103  
   104  type mockNoopPlugin struct {
   105  	uuid string
   106  }
   107  
   108  func (p *mockNoopPlugin) Name() string {
   109  	return p.uuid
   110  }
   111  
   112  func NewMockNoopPluginFactory() PluginFactory {
   113  	uuid := uuid.New().String()
   114  	return func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
   115  		return &mockNoopPlugin{uuid}, nil
   116  	}
   117  }
   118  
   119  func TestMerge(t *testing.T) {
   120  	m1 := NewMockNoopPluginFactory()
   121  	m2 := NewMockNoopPluginFactory()
   122  	tests := []struct {
   123  		name            string
   124  		primaryRegistry Registry
   125  		registryToMerge Registry
   126  		expected        Registry
   127  		shouldError     bool
   128  	}{
   129  		{
   130  			name: "valid Merge",
   131  			primaryRegistry: Registry{
   132  				"pluginFactory1": m1,
   133  			},
   134  			registryToMerge: Registry{
   135  				"pluginFactory2": m2,
   136  			},
   137  			expected: Registry{
   138  				"pluginFactory1": m1,
   139  				"pluginFactory2": m2,
   140  			},
   141  			shouldError: false,
   142  		},
   143  		{
   144  			name: "Merge duplicate factories",
   145  			primaryRegistry: Registry{
   146  				"pluginFactory1": m1,
   147  			},
   148  			registryToMerge: Registry{
   149  				"pluginFactory1": m2,
   150  			},
   151  			expected: Registry{
   152  				"pluginFactory1": m1,
   153  			},
   154  			shouldError: true,
   155  		},
   156  	}
   157  
   158  	for _, scenario := range tests {
   159  		t.Run(scenario.name, func(t *testing.T) {
   160  			err := scenario.primaryRegistry.Merge(scenario.registryToMerge)
   161  
   162  			if (err == nil) == scenario.shouldError {
   163  				t.Errorf("Merge() shouldError is: %v, however err is: %v.", scenario.shouldError, err)
   164  				return
   165  			}
   166  
   167  			if !isRegistryEqual(scenario.expected, scenario.primaryRegistry) {
   168  				t.Errorf("Merge(). Expected %v. Got %v instead.", scenario.expected, scenario.primaryRegistry)
   169  			}
   170  		})
   171  	}
   172  }
   173  
   174  func TestRegister(t *testing.T) {
   175  	m1 := NewMockNoopPluginFactory()
   176  	m2 := NewMockNoopPluginFactory()
   177  	tests := []struct {
   178  		name              string
   179  		registry          Registry
   180  		nameToRegister    string
   181  		factoryToRegister PluginFactory
   182  		expected          Registry
   183  		shouldError       bool
   184  	}{
   185  		{
   186  			name:              "valid Register",
   187  			registry:          Registry{},
   188  			nameToRegister:    "pluginFactory1",
   189  			factoryToRegister: m1,
   190  			expected: Registry{
   191  				"pluginFactory1": m1,
   192  			},
   193  			shouldError: false,
   194  		},
   195  		{
   196  			name: "Register duplicate factories",
   197  			registry: Registry{
   198  				"pluginFactory1": m1,
   199  			},
   200  			nameToRegister:    "pluginFactory1",
   201  			factoryToRegister: m2,
   202  			expected: Registry{
   203  				"pluginFactory1": m1,
   204  			},
   205  			shouldError: true,
   206  		},
   207  	}
   208  
   209  	for _, scenario := range tests {
   210  		t.Run(scenario.name, func(t *testing.T) {
   211  			err := scenario.registry.Register(scenario.nameToRegister, scenario.factoryToRegister)
   212  
   213  			if (err == nil) == scenario.shouldError {
   214  				t.Errorf("Register() shouldError is: %v however err is: %v.", scenario.shouldError, err)
   215  				return
   216  			}
   217  
   218  			if !isRegistryEqual(scenario.expected, scenario.registry) {
   219  				t.Errorf("Register(). Expected %v. Got %v instead.", scenario.expected, scenario.registry)
   220  			}
   221  		})
   222  	}
   223  }
   224  
   225  func TestUnregister(t *testing.T) {
   226  	m1 := NewMockNoopPluginFactory()
   227  	m2 := NewMockNoopPluginFactory()
   228  	tests := []struct {
   229  		name             string
   230  		registry         Registry
   231  		nameToUnregister string
   232  		expected         Registry
   233  		shouldError      bool
   234  	}{
   235  		{
   236  			name: "valid Unregister",
   237  			registry: Registry{
   238  				"pluginFactory1": m1,
   239  				"pluginFactory2": m2,
   240  			},
   241  			nameToUnregister: "pluginFactory1",
   242  			expected: Registry{
   243  				"pluginFactory2": m2,
   244  			},
   245  			shouldError: false,
   246  		},
   247  		{
   248  			name:             "Unregister non-existent plugin factory",
   249  			registry:         Registry{},
   250  			nameToUnregister: "pluginFactory1",
   251  			expected:         Registry{},
   252  			shouldError:      true,
   253  		},
   254  	}
   255  
   256  	for _, scenario := range tests {
   257  		t.Run(scenario.name, func(t *testing.T) {
   258  			err := scenario.registry.Unregister(scenario.nameToUnregister)
   259  
   260  			if (err == nil) == scenario.shouldError {
   261  				t.Errorf("Unregister() shouldError is: %v however err is: %v.", scenario.shouldError, err)
   262  				return
   263  			}
   264  
   265  			if !isRegistryEqual(scenario.expected, scenario.registry) {
   266  				t.Errorf("Unregister(). Expected %v. Got %v instead.", scenario.expected, scenario.registry)
   267  			}
   268  		})
   269  	}
   270  }