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 }