...

Source file src/k8s.io/kubernetes/pkg/scheduler/framework/runtime/framework_test.go

Documentation: k8s.io/kubernetes/pkg/scheduler/framework/runtime

     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  	"errors"
    22  	"fmt"
    23  	"reflect"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/google/go-cmp/cmp"
    29  	v1 "k8s.io/api/core/v1"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/runtime"
    32  	"k8s.io/apimachinery/pkg/types"
    33  	"k8s.io/apimachinery/pkg/util/sets"
    34  	"k8s.io/component-base/metrics/testutil"
    35  	"k8s.io/klog/v2/ktesting"
    36  	"k8s.io/kubernetes/pkg/scheduler/apis/config"
    37  	"k8s.io/kubernetes/pkg/scheduler/framework"
    38  	internalqueue "k8s.io/kubernetes/pkg/scheduler/internal/queue"
    39  	"k8s.io/kubernetes/pkg/scheduler/metrics"
    40  	"k8s.io/utils/ptr"
    41  )
    42  
    43  const (
    44  	preEnqueuePlugin                  = "preEnqueue-plugin"
    45  	queueSortPlugin                   = "no-op-queue-sort-plugin"
    46  	scoreWithNormalizePlugin1         = "score-with-normalize-plugin-1"
    47  	scoreWithNormalizePlugin2         = "score-with-normalize-plugin-2"
    48  	scorePlugin1                      = "score-plugin-1"
    49  	scorePlugin2                      = "score-plugin-2"
    50  	pluginNotImplementingScore        = "plugin-not-implementing-score"
    51  	preFilterPluginName               = "prefilter-plugin"
    52  	preFilterWithExtensionsPluginName = "prefilter-with-extensions-plugin"
    53  	duplicatePluginName               = "duplicate-plugin"
    54  	testPlugin                        = "test-plugin"
    55  	permitPlugin                      = "permit-plugin"
    56  	bindPlugin                        = "bind-plugin"
    57  	testCloseErrorPlugin              = "test-close-error-plugin"
    58  
    59  	testProfileName              = "test-profile"
    60  	testPercentageOfNodesToScore = 35
    61  	nodeName                     = "testNode"
    62  
    63  	injectReason       = "injected status"
    64  	injectFilterReason = "injected filter status"
    65  )
    66  
    67  // TestScoreWithNormalizePlugin implements ScoreWithNormalizePlugin interface.
    68  // TestScorePlugin only implements ScorePlugin interface.
    69  var _ framework.ScorePlugin = &TestScoreWithNormalizePlugin{}
    70  var _ framework.ScorePlugin = &TestScorePlugin{}
    71  
    72  var cmpOpts = []cmp.Option{
    73  	cmp.Comparer(func(s1 *framework.Status, s2 *framework.Status) bool {
    74  		if s1 == nil || s2 == nil {
    75  			return s1.IsSuccess() && s2.IsSuccess()
    76  		}
    77  		return s1.Code() == s2.Code() && s1.Plugin() == s2.Plugin() && s1.Message() == s2.Message()
    78  	}),
    79  }
    80  
    81  func newScoreWithNormalizePlugin1(_ context.Context, injArgs runtime.Object, f framework.Handle) (framework.Plugin, error) {
    82  	var inj injectedResult
    83  	if err := DecodeInto(injArgs, &inj); err != nil {
    84  		return nil, err
    85  	}
    86  	return &TestScoreWithNormalizePlugin{scoreWithNormalizePlugin1, inj}, nil
    87  }
    88  
    89  func newScoreWithNormalizePlugin2(_ context.Context, injArgs runtime.Object, f framework.Handle) (framework.Plugin, error) {
    90  	var inj injectedResult
    91  	if err := DecodeInto(injArgs, &inj); err != nil {
    92  		return nil, err
    93  	}
    94  	return &TestScoreWithNormalizePlugin{scoreWithNormalizePlugin2, inj}, nil
    95  }
    96  
    97  func newScorePlugin1(_ context.Context, injArgs runtime.Object, f framework.Handle) (framework.Plugin, error) {
    98  	var inj injectedResult
    99  	if err := DecodeInto(injArgs, &inj); err != nil {
   100  		return nil, err
   101  	}
   102  	return &TestScorePlugin{scorePlugin1, inj}, nil
   103  }
   104  
   105  func newScorePlugin2(_ context.Context, injArgs runtime.Object, f framework.Handle) (framework.Plugin, error) {
   106  	var inj injectedResult
   107  	if err := DecodeInto(injArgs, &inj); err != nil {
   108  		return nil, err
   109  	}
   110  	return &TestScorePlugin{scorePlugin2, inj}, nil
   111  }
   112  
   113  func newPluginNotImplementingScore(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
   114  	return &PluginNotImplementingScore{}, nil
   115  }
   116  
   117  type TestScoreWithNormalizePlugin struct {
   118  	name string
   119  	inj  injectedResult
   120  }
   121  
   122  func (pl *TestScoreWithNormalizePlugin) Name() string {
   123  	return pl.name
   124  }
   125  
   126  func (pl *TestScoreWithNormalizePlugin) NormalizeScore(ctx context.Context, state *framework.CycleState, pod *v1.Pod, scores framework.NodeScoreList) *framework.Status {
   127  	return injectNormalizeRes(pl.inj, scores)
   128  }
   129  
   130  func (pl *TestScoreWithNormalizePlugin) Score(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (int64, *framework.Status) {
   131  	return setScoreRes(pl.inj)
   132  }
   133  
   134  func (pl *TestScoreWithNormalizePlugin) ScoreExtensions() framework.ScoreExtensions {
   135  	return pl
   136  }
   137  
   138  // TestScorePlugin only implements ScorePlugin interface.
   139  type TestScorePlugin struct {
   140  	name string
   141  	inj  injectedResult
   142  }
   143  
   144  func (pl *TestScorePlugin) Name() string {
   145  	return pl.name
   146  }
   147  
   148  func (pl *TestScorePlugin) PreScore(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodes []*framework.NodeInfo) *framework.Status {
   149  	return framework.NewStatus(framework.Code(pl.inj.PreScoreStatus), injectReason)
   150  }
   151  
   152  func (pl *TestScorePlugin) Score(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (int64, *framework.Status) {
   153  	return setScoreRes(pl.inj)
   154  }
   155  
   156  func (pl *TestScorePlugin) ScoreExtensions() framework.ScoreExtensions {
   157  	return nil
   158  }
   159  
   160  // PluginNotImplementingScore doesn't implement the ScorePlugin interface.
   161  type PluginNotImplementingScore struct{}
   162  
   163  func (pl *PluginNotImplementingScore) Name() string {
   164  	return pluginNotImplementingScore
   165  }
   166  
   167  func newTestPlugin(_ context.Context, injArgs runtime.Object, f framework.Handle) (framework.Plugin, error) {
   168  	return &TestPlugin{name: testPlugin}, nil
   169  }
   170  
   171  // TestPlugin implements all Plugin interfaces.
   172  type TestPlugin struct {
   173  	name string
   174  	inj  injectedResult
   175  }
   176  
   177  func (pl *TestPlugin) AddPod(ctx context.Context, state *framework.CycleState, podToSchedule *v1.Pod, podInfoToAdd *framework.PodInfo, nodeInfo *framework.NodeInfo) *framework.Status {
   178  	return framework.NewStatus(framework.Code(pl.inj.PreFilterAddPodStatus), injectReason)
   179  }
   180  func (pl *TestPlugin) RemovePod(ctx context.Context, state *framework.CycleState, podToSchedule *v1.Pod, podInfoToRemove *framework.PodInfo, nodeInfo *framework.NodeInfo) *framework.Status {
   181  	return framework.NewStatus(framework.Code(pl.inj.PreFilterRemovePodStatus), injectReason)
   182  }
   183  
   184  func (pl *TestPlugin) Name() string {
   185  	return pl.name
   186  }
   187  
   188  func (pl *TestPlugin) Less(*framework.QueuedPodInfo, *framework.QueuedPodInfo) bool {
   189  	return false
   190  }
   191  
   192  func (pl *TestPlugin) Score(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (int64, *framework.Status) {
   193  	return 0, framework.NewStatus(framework.Code(pl.inj.ScoreStatus), injectReason)
   194  }
   195  
   196  func (pl *TestPlugin) ScoreExtensions() framework.ScoreExtensions {
   197  	return nil
   198  }
   199  
   200  func (pl *TestPlugin) PreFilter(ctx context.Context, state *framework.CycleState, p *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
   201  	return pl.inj.PreFilterResult, framework.NewStatus(framework.Code(pl.inj.PreFilterStatus), injectReason)
   202  }
   203  
   204  func (pl *TestPlugin) PreFilterExtensions() framework.PreFilterExtensions {
   205  	return pl
   206  }
   207  
   208  func (pl *TestPlugin) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
   209  	return framework.NewStatus(framework.Code(pl.inj.FilterStatus), injectFilterReason)
   210  }
   211  
   212  func (pl *TestPlugin) PostFilter(_ context.Context, _ *framework.CycleState, _ *v1.Pod, _ framework.NodeToStatusMap) (*framework.PostFilterResult, *framework.Status) {
   213  	return nil, framework.NewStatus(framework.Code(pl.inj.PostFilterStatus), injectReason)
   214  }
   215  
   216  func (pl *TestPlugin) PreScore(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodes []*framework.NodeInfo) *framework.Status {
   217  	return framework.NewStatus(framework.Code(pl.inj.PreScoreStatus), injectReason)
   218  }
   219  
   220  func (pl *TestPlugin) Reserve(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) *framework.Status {
   221  	return framework.NewStatus(framework.Code(pl.inj.ReserveStatus), injectReason)
   222  }
   223  
   224  func (pl *TestPlugin) Unreserve(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) {
   225  }
   226  
   227  func (pl *TestPlugin) PreBind(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) *framework.Status {
   228  	return framework.NewStatus(framework.Code(pl.inj.PreBindStatus), injectReason)
   229  }
   230  
   231  func (pl *TestPlugin) PostBind(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) {
   232  }
   233  
   234  func (pl *TestPlugin) Permit(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (*framework.Status, time.Duration) {
   235  	return framework.NewStatus(framework.Code(pl.inj.PermitStatus), injectReason), time.Duration(0)
   236  }
   237  
   238  func (pl *TestPlugin) Bind(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) *framework.Status {
   239  	return framework.NewStatus(framework.Code(pl.inj.BindStatus), injectReason)
   240  }
   241  
   242  func newTestCloseErrorPlugin(_ context.Context, injArgs runtime.Object, f framework.Handle) (framework.Plugin, error) {
   243  	return &TestCloseErrorPlugin{name: testCloseErrorPlugin}, nil
   244  }
   245  
   246  // TestCloseErrorPlugin implements for Close test.
   247  type TestCloseErrorPlugin struct {
   248  	name string
   249  }
   250  
   251  func (pl *TestCloseErrorPlugin) Name() string {
   252  	return pl.name
   253  }
   254  
   255  var errClose = errors.New("close err")
   256  
   257  func (pl *TestCloseErrorPlugin) Close() error {
   258  	return errClose
   259  }
   260  
   261  // TestPreFilterPlugin only implements PreFilterPlugin interface.
   262  type TestPreFilterPlugin struct {
   263  	PreFilterCalled int
   264  }
   265  
   266  func (pl *TestPreFilterPlugin) Name() string {
   267  	return preFilterPluginName
   268  }
   269  
   270  func (pl *TestPreFilterPlugin) PreFilter(ctx context.Context, state *framework.CycleState, p *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
   271  	pl.PreFilterCalled++
   272  	return nil, nil
   273  }
   274  
   275  func (pl *TestPreFilterPlugin) PreFilterExtensions() framework.PreFilterExtensions {
   276  	return nil
   277  }
   278  
   279  // TestPreFilterWithExtensionsPlugin implements Add/Remove interfaces.
   280  type TestPreFilterWithExtensionsPlugin struct {
   281  	PreFilterCalled int
   282  	AddCalled       int
   283  	RemoveCalled    int
   284  }
   285  
   286  func (pl *TestPreFilterWithExtensionsPlugin) Name() string {
   287  	return preFilterWithExtensionsPluginName
   288  }
   289  
   290  func (pl *TestPreFilterWithExtensionsPlugin) PreFilter(ctx context.Context, state *framework.CycleState, p *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
   291  	pl.PreFilterCalled++
   292  	return nil, nil
   293  }
   294  
   295  func (pl *TestPreFilterWithExtensionsPlugin) AddPod(ctx context.Context, state *framework.CycleState, podToSchedule *v1.Pod,
   296  	podInfoToAdd *framework.PodInfo, nodeInfo *framework.NodeInfo) *framework.Status {
   297  	pl.AddCalled++
   298  	return nil
   299  }
   300  
   301  func (pl *TestPreFilterWithExtensionsPlugin) RemovePod(ctx context.Context, state *framework.CycleState, podToSchedule *v1.Pod,
   302  	podInfoToRemove *framework.PodInfo, nodeInfo *framework.NodeInfo) *framework.Status {
   303  	pl.RemoveCalled++
   304  	return nil
   305  }
   306  
   307  func (pl *TestPreFilterWithExtensionsPlugin) PreFilterExtensions() framework.PreFilterExtensions {
   308  	return pl
   309  }
   310  
   311  type TestDuplicatePlugin struct {
   312  }
   313  
   314  func (dp *TestDuplicatePlugin) Name() string {
   315  	return duplicatePluginName
   316  }
   317  
   318  func (dp *TestDuplicatePlugin) PreFilter(ctx context.Context, state *framework.CycleState, p *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
   319  	return nil, nil
   320  }
   321  
   322  func (dp *TestDuplicatePlugin) PreFilterExtensions() framework.PreFilterExtensions {
   323  	return nil
   324  }
   325  
   326  var _ framework.PreFilterPlugin = &TestDuplicatePlugin{}
   327  
   328  func newDuplicatePlugin(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
   329  	return &TestDuplicatePlugin{}, nil
   330  }
   331  
   332  // TestPermitPlugin only implements PermitPlugin interface.
   333  type TestPermitPlugin struct {
   334  	PreFilterCalled int
   335  }
   336  
   337  func (pp *TestPermitPlugin) Name() string {
   338  	return permitPlugin
   339  }
   340  func (pp *TestPermitPlugin) Permit(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (*framework.Status, time.Duration) {
   341  	return framework.NewStatus(framework.Wait), 10 * time.Second
   342  }
   343  
   344  var _ framework.PreEnqueuePlugin = &TestPreEnqueuePlugin{}
   345  
   346  type TestPreEnqueuePlugin struct{}
   347  
   348  func (pl *TestPreEnqueuePlugin) Name() string {
   349  	return preEnqueuePlugin
   350  }
   351  
   352  func (pl *TestPreEnqueuePlugin) PreEnqueue(ctx context.Context, p *v1.Pod) *framework.Status {
   353  	return nil
   354  }
   355  
   356  var _ framework.QueueSortPlugin = &TestQueueSortPlugin{}
   357  
   358  func newQueueSortPlugin(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
   359  	return &TestQueueSortPlugin{}, nil
   360  }
   361  
   362  // TestQueueSortPlugin is a no-op implementation for QueueSort extension point.
   363  type TestQueueSortPlugin struct{}
   364  
   365  func (pl *TestQueueSortPlugin) Name() string {
   366  	return queueSortPlugin
   367  }
   368  
   369  func (pl *TestQueueSortPlugin) Less(_, _ *framework.QueuedPodInfo) bool {
   370  	return false
   371  }
   372  
   373  var _ framework.BindPlugin = &TestBindPlugin{}
   374  
   375  func newBindPlugin(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
   376  	return &TestBindPlugin{}, nil
   377  }
   378  
   379  // TestBindPlugin is a no-op implementation for Bind extension point.
   380  type TestBindPlugin struct{}
   381  
   382  func (t TestBindPlugin) Name() string {
   383  	return bindPlugin
   384  }
   385  
   386  func (t TestBindPlugin) Bind(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) *framework.Status {
   387  	return nil
   388  }
   389  
   390  // nolint:errcheck   // Ignore the error returned by Register as before
   391  var registry = func() Registry {
   392  	r := make(Registry)
   393  	r.Register(scoreWithNormalizePlugin1, newScoreWithNormalizePlugin1)
   394  	r.Register(scoreWithNormalizePlugin2, newScoreWithNormalizePlugin2)
   395  	r.Register(scorePlugin1, newScorePlugin1)
   396  	r.Register(scorePlugin2, newScorePlugin2)
   397  	r.Register(pluginNotImplementingScore, newPluginNotImplementingScore)
   398  	r.Register(duplicatePluginName, newDuplicatePlugin)
   399  	r.Register(testPlugin, newTestPlugin)
   400  	r.Register(queueSortPlugin, newQueueSortPlugin)
   401  	r.Register(bindPlugin, newBindPlugin)
   402  	r.Register(testCloseErrorPlugin, newTestCloseErrorPlugin)
   403  	return r
   404  }()
   405  
   406  var defaultWeights = map[string]int32{
   407  	scoreWithNormalizePlugin1: 1,
   408  	scoreWithNormalizePlugin2: 2,
   409  	scorePlugin1:              1,
   410  }
   411  
   412  var state = &framework.CycleState{}
   413  
   414  // Pod is only used for logging errors.
   415  var pod = &v1.Pod{}
   416  var node = &v1.Node{
   417  	ObjectMeta: metav1.ObjectMeta{
   418  		Name: nodeName,
   419  	},
   420  }
   421  var lowPriority, highPriority = int32(0), int32(1000)
   422  var lowPriorityPod = &v1.Pod{
   423  	ObjectMeta: metav1.ObjectMeta{UID: "low"},
   424  	Spec:       v1.PodSpec{Priority: &lowPriority},
   425  }
   426  var highPriorityPod = &v1.Pod{
   427  	ObjectMeta: metav1.ObjectMeta{UID: "high"},
   428  	Spec:       v1.PodSpec{Priority: &highPriority},
   429  }
   430  var nodes = []*v1.Node{
   431  	{ObjectMeta: metav1.ObjectMeta{Name: "node1"}},
   432  	{ObjectMeta: metav1.ObjectMeta{Name: "node2"}},
   433  }
   434  
   435  var (
   436  	errInjectedStatus       = errors.New(injectReason)
   437  	errInjectedFilterStatus = errors.New(injectFilterReason)
   438  )
   439  
   440  func newFrameworkWithQueueSortAndBind(ctx context.Context, r Registry, profile config.KubeSchedulerProfile, opts ...Option) (framework.Framework, error) {
   441  	if _, ok := r[queueSortPlugin]; !ok {
   442  		r[queueSortPlugin] = newQueueSortPlugin
   443  	}
   444  	if _, ok := r[bindPlugin]; !ok {
   445  		r[bindPlugin] = newBindPlugin
   446  	}
   447  
   448  	if len(profile.Plugins.QueueSort.Enabled) == 0 {
   449  		profile.Plugins.QueueSort.Enabled = append(profile.Plugins.QueueSort.Enabled, config.Plugin{Name: queueSortPlugin})
   450  	}
   451  	if len(profile.Plugins.Bind.Enabled) == 0 {
   452  		profile.Plugins.Bind.Enabled = append(profile.Plugins.Bind.Enabled, config.Plugin{Name: bindPlugin})
   453  	}
   454  	return NewFramework(ctx, r, &profile, opts...)
   455  }
   456  
   457  func TestInitFrameworkWithScorePlugins(t *testing.T) {
   458  	tests := []struct {
   459  		name    string
   460  		plugins *config.Plugins
   461  		// If initErr is true, we expect framework initialization to fail.
   462  		initErr bool
   463  	}{
   464  		{
   465  			name:    "enabled Score plugin doesn't exist in registry",
   466  			plugins: buildScoreConfigDefaultWeights("notExist"),
   467  			initErr: true,
   468  		},
   469  		{
   470  			name:    "enabled Score plugin doesn't extend the ScorePlugin interface",
   471  			plugins: buildScoreConfigDefaultWeights(pluginNotImplementingScore),
   472  			initErr: true,
   473  		},
   474  		{
   475  			name:    "Score plugins are nil",
   476  			plugins: &config.Plugins{},
   477  		},
   478  		{
   479  			name:    "enabled Score plugin list is empty",
   480  			plugins: buildScoreConfigDefaultWeights(),
   481  		},
   482  		{
   483  			name:    "enabled plugin only implements ScorePlugin interface",
   484  			plugins: buildScoreConfigDefaultWeights(scorePlugin1),
   485  		},
   486  		{
   487  			name:    "enabled plugin implements ScoreWithNormalizePlugin interface",
   488  			plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
   489  		},
   490  	}
   491  
   492  	for _, tt := range tests {
   493  		t.Run(tt.name, func(t *testing.T) {
   494  			profile := config.KubeSchedulerProfile{Plugins: tt.plugins}
   495  			_, ctx := ktesting.NewTestContext(t)
   496  			ctx, cancel := context.WithCancel(ctx)
   497  			defer cancel()
   498  			_, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
   499  			if tt.initErr && err == nil {
   500  				t.Fatal("Framework initialization should fail")
   501  			}
   502  			if !tt.initErr && err != nil {
   503  				t.Fatalf("Failed to create framework for testing: %v", err)
   504  			}
   505  		})
   506  	}
   507  }
   508  
   509  func TestNewFrameworkErrors(t *testing.T) {
   510  	tests := []struct {
   511  		name      string
   512  		plugins   *config.Plugins
   513  		pluginCfg []config.PluginConfig
   514  		wantErr   string
   515  	}{
   516  		{
   517  			name: "duplicate plugin name",
   518  			plugins: &config.Plugins{
   519  				PreFilter: config.PluginSet{
   520  					Enabled: []config.Plugin{
   521  						{Name: duplicatePluginName, Weight: 1},
   522  						{Name: duplicatePluginName, Weight: 1},
   523  					},
   524  				},
   525  			},
   526  			pluginCfg: []config.PluginConfig{
   527  				{Name: duplicatePluginName},
   528  			},
   529  			wantErr: "already registered",
   530  		},
   531  		{
   532  			name: "duplicate plugin config",
   533  			plugins: &config.Plugins{
   534  				PreFilter: config.PluginSet{
   535  					Enabled: []config.Plugin{
   536  						{Name: duplicatePluginName, Weight: 1},
   537  					},
   538  				},
   539  			},
   540  			pluginCfg: []config.PluginConfig{
   541  				{Name: duplicatePluginName},
   542  				{Name: duplicatePluginName},
   543  			},
   544  			wantErr: "repeated config for plugin",
   545  		},
   546  	}
   547  
   548  	for _, tc := range tests {
   549  		t.Run(tc.name, func(t *testing.T) {
   550  			_, ctx := ktesting.NewTestContext(t)
   551  			ctx, cancel := context.WithCancel(ctx)
   552  			defer cancel()
   553  			profile := &config.KubeSchedulerProfile{
   554  				Plugins:      tc.plugins,
   555  				PluginConfig: tc.pluginCfg,
   556  			}
   557  			_, err := NewFramework(ctx, registry, profile)
   558  			if err == nil || !strings.Contains(err.Error(), tc.wantErr) {
   559  				t.Errorf("Unexpected error, got %v, expect: %s", err, tc.wantErr)
   560  			}
   561  		})
   562  	}
   563  }
   564  
   565  func TestNewFrameworkMultiPointExpansion(t *testing.T) {
   566  	tests := []struct {
   567  		name        string
   568  		plugins     *config.Plugins
   569  		wantPlugins *config.Plugins
   570  		wantErr     string
   571  	}{
   572  		{
   573  			name: "plugin expansion",
   574  			plugins: &config.Plugins{
   575  				MultiPoint: config.PluginSet{
   576  					Enabled: []config.Plugin{
   577  						{Name: testPlugin, Weight: 5},
   578  					},
   579  				},
   580  			},
   581  			wantPlugins: &config.Plugins{
   582  				QueueSort:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   583  				PreFilter:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   584  				Filter:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   585  				PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   586  				PreScore:   config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   587  				Score:      config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin, Weight: 5}}},
   588  				Reserve:    config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   589  				Permit:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   590  				PreBind:    config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   591  				Bind:       config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   592  				PostBind:   config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   593  			},
   594  		},
   595  		{
   596  			name: "disable MultiPoint plugin at some extension points",
   597  			plugins: &config.Plugins{
   598  				MultiPoint: config.PluginSet{
   599  					Enabled: []config.Plugin{
   600  						{Name: testPlugin},
   601  					},
   602  				},
   603  				PreScore: config.PluginSet{
   604  					Disabled: []config.Plugin{
   605  						{Name: testPlugin},
   606  					},
   607  				},
   608  				Score: config.PluginSet{
   609  					Disabled: []config.Plugin{
   610  						{Name: testPlugin},
   611  					},
   612  				},
   613  			},
   614  			wantPlugins: &config.Plugins{
   615  				QueueSort:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   616  				PreFilter:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   617  				Filter:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   618  				PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   619  				Reserve:    config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   620  				Permit:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   621  				PreBind:    config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   622  				Bind:       config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   623  				PostBind:   config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   624  			},
   625  		},
   626  		{
   627  			name: "Multiple MultiPoint plugins",
   628  			plugins: &config.Plugins{
   629  				MultiPoint: config.PluginSet{
   630  					Enabled: []config.Plugin{
   631  						{Name: testPlugin},
   632  						{Name: scorePlugin1},
   633  					},
   634  				},
   635  			},
   636  			wantPlugins: &config.Plugins{
   637  				QueueSort:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   638  				PreFilter:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   639  				Filter:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   640  				PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   641  				PreScore: config.PluginSet{Enabled: []config.Plugin{
   642  					{Name: testPlugin},
   643  					{Name: scorePlugin1},
   644  				}},
   645  				Score: config.PluginSet{Enabled: []config.Plugin{
   646  					{Name: testPlugin, Weight: 1},
   647  					{Name: scorePlugin1, Weight: 1},
   648  				}},
   649  				Reserve:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   650  				Permit:   config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   651  				PreBind:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   652  				Bind:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   653  				PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   654  			},
   655  		},
   656  		{
   657  			name: "disable MultiPoint extension",
   658  			plugins: &config.Plugins{
   659  				MultiPoint: config.PluginSet{
   660  					Enabled: []config.Plugin{
   661  						{Name: testPlugin},
   662  						{Name: scorePlugin1},
   663  					},
   664  				},
   665  				PreScore: config.PluginSet{
   666  					Disabled: []config.Plugin{
   667  						{Name: "*"},
   668  					},
   669  				},
   670  			},
   671  			wantPlugins: &config.Plugins{
   672  				QueueSort:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   673  				PreFilter:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   674  				Filter:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   675  				PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   676  				Score: config.PluginSet{Enabled: []config.Plugin{
   677  					{Name: testPlugin, Weight: 1},
   678  					{Name: scorePlugin1, Weight: 1},
   679  				}},
   680  				Reserve:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   681  				Permit:   config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   682  				PreBind:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   683  				Bind:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   684  				PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   685  			},
   686  		},
   687  		{
   688  			name: "Reorder MultiPoint plugins (specified extension takes precedence)",
   689  			plugins: &config.Plugins{
   690  				MultiPoint: config.PluginSet{
   691  					Enabled: []config.Plugin{
   692  						{Name: scoreWithNormalizePlugin1},
   693  						{Name: testPlugin},
   694  						{Name: scorePlugin1},
   695  					},
   696  				},
   697  				Score: config.PluginSet{
   698  					Enabled: []config.Plugin{
   699  						{Name: scorePlugin1},
   700  						{Name: testPlugin},
   701  					},
   702  				},
   703  			},
   704  			wantPlugins: &config.Plugins{
   705  				QueueSort:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   706  				PreFilter:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   707  				Filter:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   708  				PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   709  				PreScore: config.PluginSet{Enabled: []config.Plugin{
   710  					{Name: testPlugin},
   711  					{Name: scorePlugin1},
   712  				}},
   713  				Score: config.PluginSet{Enabled: []config.Plugin{
   714  					{Name: scorePlugin1, Weight: 1},
   715  					{Name: testPlugin, Weight: 1},
   716  					{Name: scoreWithNormalizePlugin1, Weight: 1},
   717  				}},
   718  				Reserve:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   719  				Permit:   config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   720  				PreBind:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   721  				Bind:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   722  				PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   723  			},
   724  		},
   725  		{
   726  			name: "Reorder MultiPoint plugins (specified extension only takes precedence when it exists in MultiPoint)",
   727  			plugins: &config.Plugins{
   728  				MultiPoint: config.PluginSet{
   729  					Enabled: []config.Plugin{
   730  						{Name: testPlugin},
   731  						{Name: scorePlugin1},
   732  					},
   733  				},
   734  				Score: config.PluginSet{
   735  					Enabled: []config.Plugin{
   736  						{Name: scoreWithNormalizePlugin1},
   737  						{Name: scorePlugin1},
   738  						{Name: testPlugin},
   739  					},
   740  				},
   741  			},
   742  			wantPlugins: &config.Plugins{
   743  				QueueSort:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   744  				PreFilter:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   745  				Filter:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   746  				PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   747  				PreScore: config.PluginSet{Enabled: []config.Plugin{
   748  					{Name: testPlugin},
   749  					{Name: scorePlugin1},
   750  				}},
   751  				Score: config.PluginSet{Enabled: []config.Plugin{
   752  					{Name: scorePlugin1, Weight: 1},
   753  					{Name: testPlugin, Weight: 1},
   754  					{Name: scoreWithNormalizePlugin1, Weight: 1},
   755  				}},
   756  				Reserve:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   757  				Permit:   config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   758  				PreBind:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   759  				Bind:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   760  				PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   761  			},
   762  		},
   763  		{
   764  			name: "Override MultiPoint plugins weights",
   765  			plugins: &config.Plugins{
   766  				MultiPoint: config.PluginSet{
   767  					Enabled: []config.Plugin{
   768  						{Name: testPlugin},
   769  						{Name: scorePlugin1},
   770  					},
   771  				},
   772  				Score: config.PluginSet{
   773  					Enabled: []config.Plugin{
   774  						{Name: scorePlugin1, Weight: 5},
   775  						{Name: testPlugin, Weight: 3},
   776  					},
   777  				},
   778  			},
   779  			wantPlugins: &config.Plugins{
   780  				QueueSort:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   781  				PreFilter:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   782  				Filter:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   783  				PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   784  				PreScore: config.PluginSet{Enabled: []config.Plugin{
   785  					{Name: testPlugin},
   786  					{Name: scorePlugin1},
   787  				}},
   788  				Score: config.PluginSet{Enabled: []config.Plugin{
   789  					{Name: scorePlugin1, Weight: 5},
   790  					{Name: testPlugin, Weight: 3},
   791  				}},
   792  				Reserve:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   793  				Permit:   config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   794  				PreBind:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   795  				Bind:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   796  				PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   797  			},
   798  		},
   799  		{
   800  			name: "disable and enable MultiPoint plugins with '*'",
   801  			plugins: &config.Plugins{
   802  				MultiPoint: config.PluginSet{
   803  					Enabled: []config.Plugin{
   804  						{Name: queueSortPlugin},
   805  						{Name: bindPlugin},
   806  						{Name: scorePlugin1},
   807  					},
   808  					Disabled: []config.Plugin{
   809  						{Name: "*"},
   810  					},
   811  				},
   812  			},
   813  			wantPlugins: &config.Plugins{
   814  				QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: queueSortPlugin}}},
   815  				PreScore: config.PluginSet{Enabled: []config.Plugin{
   816  					{Name: scorePlugin1},
   817  				}},
   818  				Score: config.PluginSet{Enabled: []config.Plugin{
   819  					{Name: scorePlugin1, Weight: 1},
   820  				}},
   821  				Bind: config.PluginSet{Enabled: []config.Plugin{{Name: bindPlugin}}},
   822  			},
   823  		},
   824  		{
   825  			name: "disable and enable MultiPoint plugin by name",
   826  			plugins: &config.Plugins{
   827  				MultiPoint: config.PluginSet{
   828  					Enabled: []config.Plugin{
   829  						{Name: bindPlugin},
   830  						{Name: queueSortPlugin},
   831  						{Name: scorePlugin1},
   832  					},
   833  					Disabled: []config.Plugin{
   834  						{Name: scorePlugin1},
   835  					},
   836  				},
   837  			},
   838  			wantPlugins: &config.Plugins{
   839  				QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: queueSortPlugin}}},
   840  				PreScore: config.PluginSet{Enabled: []config.Plugin{
   841  					{Name: scorePlugin1},
   842  				}},
   843  				Score: config.PluginSet{Enabled: []config.Plugin{
   844  					{Name: scorePlugin1, Weight: 1},
   845  				}},
   846  				Bind: config.PluginSet{Enabled: []config.Plugin{{Name: bindPlugin}}},
   847  			},
   848  		},
   849  		{
   850  			name: "Expect 'already registered' error",
   851  			plugins: &config.Plugins{
   852  				MultiPoint: config.PluginSet{
   853  					Enabled: []config.Plugin{
   854  						{Name: testPlugin},
   855  						{Name: testPlugin},
   856  					},
   857  				},
   858  			},
   859  			wantErr: "already registered",
   860  		},
   861  		{
   862  			name: "Override MultiPoint plugins weights and avoid a plugin being loaded multiple times",
   863  			plugins: &config.Plugins{
   864  				MultiPoint: config.PluginSet{
   865  					Enabled: []config.Plugin{
   866  						{Name: testPlugin},
   867  						{Name: scorePlugin1},
   868  					},
   869  				},
   870  				Score: config.PluginSet{
   871  					Enabled: []config.Plugin{
   872  						{Name: scorePlugin1, Weight: 5},
   873  						{Name: scorePlugin2, Weight: 5},
   874  						{Name: testPlugin, Weight: 3},
   875  					},
   876  				},
   877  			},
   878  			wantPlugins: &config.Plugins{
   879  				QueueSort:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   880  				PreFilter:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   881  				Filter:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   882  				PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   883  				PreScore: config.PluginSet{Enabled: []config.Plugin{
   884  					{Name: testPlugin},
   885  					{Name: scorePlugin1},
   886  				}},
   887  				Score: config.PluginSet{Enabled: []config.Plugin{
   888  					{Name: scorePlugin1, Weight: 5},
   889  					{Name: testPlugin, Weight: 3},
   890  					{Name: scorePlugin2, Weight: 5},
   891  				}},
   892  				Reserve:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   893  				Permit:   config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   894  				PreBind:  config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   895  				Bind:     config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   896  				PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
   897  			},
   898  		},
   899  	}
   900  
   901  	for _, tc := range tests {
   902  		t.Run(tc.name, func(t *testing.T) {
   903  			_, ctx := ktesting.NewTestContext(t)
   904  			ctx, cancel := context.WithCancel(ctx)
   905  			defer cancel()
   906  			fw, err := NewFramework(ctx, registry, &config.KubeSchedulerProfile{Plugins: tc.plugins})
   907  			if err != nil {
   908  				if tc.wantErr == "" || !strings.Contains(err.Error(), tc.wantErr) {
   909  					t.Fatalf("Unexpected error, got %v, expect: %s", err, tc.wantErr)
   910  				}
   911  			} else {
   912  				if tc.wantErr != "" {
   913  					t.Fatalf("Unexpected error, got %v, expect: %s", err, tc.wantErr)
   914  				}
   915  			}
   916  
   917  			if tc.wantErr == "" {
   918  				if diff := cmp.Diff(tc.wantPlugins, fw.ListPlugins()); diff != "" {
   919  					t.Fatalf("Unexpected eventToPlugin map (-want,+got):%s", diff)
   920  				}
   921  			}
   922  		})
   923  	}
   924  }
   925  
   926  func TestPreEnqueuePlugins(t *testing.T) {
   927  	tests := []struct {
   928  		name    string
   929  		plugins []framework.Plugin
   930  		want    []framework.PreEnqueuePlugin
   931  	}{
   932  		{
   933  			name: "no PreEnqueuePlugin registered",
   934  		},
   935  		{
   936  			name: "one PreEnqueuePlugin registered",
   937  			plugins: []framework.Plugin{
   938  				&TestPreEnqueuePlugin{},
   939  			},
   940  			want: []framework.PreEnqueuePlugin{
   941  				&TestPreEnqueuePlugin{},
   942  			},
   943  		},
   944  	}
   945  
   946  	for _, tt := range tests {
   947  		t.Run(tt.name, func(t *testing.T) {
   948  			registry := Registry{}
   949  			cfgPls := &config.Plugins{}
   950  			for _, pl := range tt.plugins {
   951  				// register all plugins
   952  				tmpPl := pl
   953  				if err := registry.Register(pl.Name(),
   954  					func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
   955  						return tmpPl, nil
   956  					}); err != nil {
   957  					t.Fatalf("fail to register preEnqueue plugin (%s)", pl.Name())
   958  				}
   959  				// append plugins to filter pluginset
   960  				cfgPls.PreEnqueue.Enabled = append(
   961  					cfgPls.PreEnqueue.Enabled,
   962  					config.Plugin{Name: pl.Name()},
   963  				)
   964  			}
   965  			profile := config.KubeSchedulerProfile{Plugins: cfgPls}
   966  			ctx, cancel := context.WithCancel(context.Background())
   967  			defer cancel()
   968  			f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
   969  			if err != nil {
   970  				t.Fatalf("fail to create framework: %s", err)
   971  			}
   972  
   973  			got := f.PreEnqueuePlugins()
   974  			if !reflect.DeepEqual(got, tt.want) {
   975  				t.Errorf("PreEnqueuePlugins(): want %v, but got %v", tt.want, got)
   976  			}
   977  		})
   978  	}
   979  }
   980  
   981  func TestRunPreScorePlugins(t *testing.T) {
   982  	tests := []struct {
   983  		name               string
   984  		plugins            []*TestPlugin
   985  		wantSkippedPlugins sets.Set[string]
   986  		wantStatusCode     framework.Code
   987  	}{
   988  		{
   989  			name: "all PreScorePlugins returned success",
   990  			plugins: []*TestPlugin{
   991  				{
   992  					name: "success1",
   993  				},
   994  				{
   995  					name: "success2",
   996  				},
   997  			},
   998  			wantStatusCode: framework.Success,
   999  		},
  1000  		{
  1001  			name: "one PreScore plugin returned success, but another PreScore plugin returned non-success",
  1002  			plugins: []*TestPlugin{
  1003  				{
  1004  					name: "success",
  1005  				},
  1006  				{
  1007  					name: "error",
  1008  					inj:  injectedResult{PreScoreStatus: int(framework.Error)},
  1009  				},
  1010  			},
  1011  			wantStatusCode: framework.Error,
  1012  		},
  1013  		{
  1014  			name: "one PreScore plugin returned skip, but another PreScore plugin returned non-success",
  1015  			plugins: []*TestPlugin{
  1016  				{
  1017  					name: "skip",
  1018  					inj:  injectedResult{PreScoreStatus: int(framework.Skip)},
  1019  				},
  1020  				{
  1021  					name: "error",
  1022  					inj:  injectedResult{PreScoreStatus: int(framework.Error)},
  1023  				},
  1024  			},
  1025  			wantSkippedPlugins: sets.New("skip"),
  1026  			wantStatusCode:     framework.Error,
  1027  		},
  1028  		{
  1029  			name: "all PreScore plugins returned skip",
  1030  			plugins: []*TestPlugin{
  1031  				{
  1032  					name: "skip1",
  1033  					inj:  injectedResult{PreScoreStatus: int(framework.Skip)},
  1034  				},
  1035  				{
  1036  					name: "skip2",
  1037  					inj:  injectedResult{PreScoreStatus: int(framework.Skip)},
  1038  				},
  1039  				{
  1040  					name: "skip3",
  1041  					inj:  injectedResult{PreScoreStatus: int(framework.Skip)},
  1042  				},
  1043  			},
  1044  			wantSkippedPlugins: sets.New("skip1", "skip2", "skip3"),
  1045  			wantStatusCode:     framework.Success,
  1046  		},
  1047  		{
  1048  			name: "some PreScore plugins returned skip",
  1049  			plugins: []*TestPlugin{
  1050  				{
  1051  					name: "skip1",
  1052  					inj:  injectedResult{PreScoreStatus: int(framework.Skip)},
  1053  				},
  1054  				{
  1055  					name: "success1",
  1056  				},
  1057  				{
  1058  					name: "skip2",
  1059  					inj:  injectedResult{PreScoreStatus: int(framework.Skip)},
  1060  				},
  1061  				{
  1062  					name: "success2",
  1063  				},
  1064  			},
  1065  			wantSkippedPlugins: sets.New("skip1", "skip2"),
  1066  			wantStatusCode:     framework.Success,
  1067  		},
  1068  	}
  1069  
  1070  	for _, tt := range tests {
  1071  		t.Run(tt.name, func(t *testing.T) {
  1072  			r := make(Registry)
  1073  			enabled := make([]config.Plugin, len(tt.plugins))
  1074  			for i, p := range tt.plugins {
  1075  				p := p
  1076  				enabled[i].Name = p.name
  1077  				if err := r.Register(p.name, func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
  1078  					return p, nil
  1079  				}); err != nil {
  1080  					t.Fatalf("fail to register PreScorePlugins plugin (%s)", p.Name())
  1081  				}
  1082  			}
  1083  
  1084  			ctx, cancel := context.WithCancel(context.Background())
  1085  			defer cancel()
  1086  
  1087  			f, err := newFrameworkWithQueueSortAndBind(
  1088  				ctx,
  1089  				r,
  1090  				config.KubeSchedulerProfile{Plugins: &config.Plugins{PreScore: config.PluginSet{Enabled: enabled}}},
  1091  			)
  1092  			if err != nil {
  1093  				t.Fatalf("Failed to create framework for testing: %v", err)
  1094  			}
  1095  
  1096  			state := framework.NewCycleState()
  1097  			status := f.RunPreScorePlugins(ctx, state, nil, nil)
  1098  			if status.Code() != tt.wantStatusCode {
  1099  				t.Errorf("wrong status code. got: %v, want: %v", status, tt.wantStatusCode)
  1100  			}
  1101  			skipped := state.SkipScorePlugins
  1102  			if d := cmp.Diff(skipped, tt.wantSkippedPlugins); d != "" {
  1103  				t.Errorf("wrong skip score plugins. got: %v, want: %v, diff: %s", skipped, tt.wantSkippedPlugins, d)
  1104  			}
  1105  		})
  1106  	}
  1107  }
  1108  
  1109  func TestRunScorePlugins(t *testing.T) {
  1110  	tests := []struct {
  1111  		name           string
  1112  		registry       Registry
  1113  		plugins        *config.Plugins
  1114  		pluginConfigs  []config.PluginConfig
  1115  		want           []framework.NodePluginScores
  1116  		skippedPlugins sets.Set[string]
  1117  		// If err is true, we expect RunScorePlugin to fail.
  1118  		err bool
  1119  	}{
  1120  		{
  1121  			name:    "no Score plugins",
  1122  			plugins: buildScoreConfigDefaultWeights(),
  1123  			want: []framework.NodePluginScores{
  1124  				{
  1125  					Name:   "node1",
  1126  					Scores: []framework.PluginScore{},
  1127  				},
  1128  				{
  1129  					Name:   "node2",
  1130  					Scores: []framework.PluginScore{},
  1131  				},
  1132  			},
  1133  		},
  1134  		{
  1135  			name:    "single Score plugin",
  1136  			plugins: buildScoreConfigDefaultWeights(scorePlugin1),
  1137  			pluginConfigs: []config.PluginConfig{
  1138  				{
  1139  					Name: scorePlugin1,
  1140  					Args: &runtime.Unknown{
  1141  						Raw: []byte(`{ "scoreRes": 1 }`),
  1142  					},
  1143  				},
  1144  			},
  1145  			// scorePlugin1 Score returns 1, weight=1, so want=1.
  1146  			want: []framework.NodePluginScores{
  1147  				{
  1148  					Name: "node1",
  1149  					Scores: []framework.PluginScore{
  1150  						{
  1151  							Name:  scorePlugin1,
  1152  							Score: 1,
  1153  						},
  1154  					},
  1155  					TotalScore: 1,
  1156  				},
  1157  				{
  1158  					Name: "node2",
  1159  					Scores: []framework.PluginScore{
  1160  						{
  1161  							Name:  scorePlugin1,
  1162  							Score: 1,
  1163  						},
  1164  					},
  1165  					TotalScore: 1,
  1166  				},
  1167  			},
  1168  		},
  1169  		{
  1170  			name: "single ScoreWithNormalize plugin",
  1171  			// registry: registry,
  1172  			plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
  1173  			pluginConfigs: []config.PluginConfig{
  1174  				{
  1175  					Name: scoreWithNormalizePlugin1,
  1176  					Args: &runtime.Unknown{
  1177  						Raw: []byte(`{ "scoreRes": 10, "normalizeRes": 5 }`),
  1178  					},
  1179  				},
  1180  			},
  1181  			// scoreWithNormalizePlugin1 Score returns 10, but NormalizeScore overrides to 5, weight=1, so want=5
  1182  			want: []framework.NodePluginScores{
  1183  				{
  1184  					Name: "node1",
  1185  					Scores: []framework.PluginScore{
  1186  						{
  1187  							Name:  scoreWithNormalizePlugin1,
  1188  							Score: 5,
  1189  						},
  1190  					},
  1191  					TotalScore: 5,
  1192  				},
  1193  				{
  1194  					Name: "node2",
  1195  					Scores: []framework.PluginScore{
  1196  						{
  1197  							Name:  scoreWithNormalizePlugin1,
  1198  							Score: 5,
  1199  						},
  1200  					},
  1201  					TotalScore: 5,
  1202  				},
  1203  			},
  1204  		},
  1205  		{
  1206  			name:    "3 Score plugins, 2 NormalizeScore plugins",
  1207  			plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1, scoreWithNormalizePlugin2),
  1208  			pluginConfigs: []config.PluginConfig{
  1209  				{
  1210  					Name: scorePlugin1,
  1211  					Args: &runtime.Unknown{
  1212  						Raw: []byte(`{ "scoreRes": 1 }`),
  1213  					},
  1214  				},
  1215  				{
  1216  					Name: scoreWithNormalizePlugin1,
  1217  					Args: &runtime.Unknown{
  1218  						Raw: []byte(`{ "scoreRes": 3, "normalizeRes": 4}`),
  1219  					},
  1220  				},
  1221  				{
  1222  					Name: scoreWithNormalizePlugin2,
  1223  					Args: &runtime.Unknown{
  1224  						Raw: []byte(`{ "scoreRes": 4, "normalizeRes": 5}`),
  1225  					},
  1226  				},
  1227  			},
  1228  			// scorePlugin1 Score returns 1, weight =1, so want=1.
  1229  			// scoreWithNormalizePlugin1 Score returns 3, but NormalizeScore overrides to 4, weight=1, so want=4.
  1230  			// scoreWithNormalizePlugin2 Score returns 4, but NormalizeScore overrides to 5, weight=2, so want=10.
  1231  			want: []framework.NodePluginScores{
  1232  				{
  1233  					Name: "node1",
  1234  					Scores: []framework.PluginScore{
  1235  						{
  1236  							Name:  scorePlugin1,
  1237  							Score: 1,
  1238  						},
  1239  						{
  1240  							Name:  scoreWithNormalizePlugin1,
  1241  							Score: 4,
  1242  						},
  1243  						{
  1244  							Name:  scoreWithNormalizePlugin2,
  1245  							Score: 10,
  1246  						},
  1247  					},
  1248  					TotalScore: 15,
  1249  				},
  1250  				{
  1251  					Name: "node2",
  1252  					Scores: []framework.PluginScore{
  1253  						{
  1254  							Name:  scorePlugin1,
  1255  							Score: 1,
  1256  						},
  1257  						{
  1258  							Name:  scoreWithNormalizePlugin1,
  1259  							Score: 4,
  1260  						},
  1261  						{
  1262  							Name:  scoreWithNormalizePlugin2,
  1263  							Score: 10,
  1264  						},
  1265  					},
  1266  					TotalScore: 15,
  1267  				},
  1268  			},
  1269  		},
  1270  		{
  1271  			name: "score fails",
  1272  			pluginConfigs: []config.PluginConfig{
  1273  				{
  1274  					Name: scoreWithNormalizePlugin1,
  1275  					Args: &runtime.Unknown{
  1276  						Raw: []byte(`{ "scoreStatus": 1 }`),
  1277  					},
  1278  				},
  1279  			},
  1280  			plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1),
  1281  			err:     true,
  1282  		},
  1283  		{
  1284  			name: "normalize fails",
  1285  			pluginConfigs: []config.PluginConfig{
  1286  				{
  1287  					Name: scoreWithNormalizePlugin1,
  1288  					Args: &runtime.Unknown{
  1289  						Raw: []byte(`{ "normalizeStatus": 1 }`),
  1290  					},
  1291  				},
  1292  			},
  1293  			plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1),
  1294  			err:     true,
  1295  		},
  1296  		{
  1297  			name:    "Score plugin return score greater than MaxNodeScore",
  1298  			plugins: buildScoreConfigDefaultWeights(scorePlugin1),
  1299  			pluginConfigs: []config.PluginConfig{
  1300  				{
  1301  					Name: scorePlugin1,
  1302  					Args: &runtime.Unknown{
  1303  						Raw: []byte(fmt.Sprintf(`{ "scoreRes": %d }`, framework.MaxNodeScore+1)),
  1304  					},
  1305  				},
  1306  			},
  1307  			err: true,
  1308  		},
  1309  		{
  1310  			name:    "Score plugin return score less than MinNodeScore",
  1311  			plugins: buildScoreConfigDefaultWeights(scorePlugin1),
  1312  			pluginConfigs: []config.PluginConfig{
  1313  				{
  1314  					Name: scorePlugin1,
  1315  					Args: &runtime.Unknown{
  1316  						Raw: []byte(fmt.Sprintf(`{ "scoreRes": %d }`, framework.MinNodeScore-1)),
  1317  					},
  1318  				},
  1319  			},
  1320  			err: true,
  1321  		},
  1322  		{
  1323  			name:    "ScoreWithNormalize plugin return score greater than MaxNodeScore",
  1324  			plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
  1325  			pluginConfigs: []config.PluginConfig{
  1326  				{
  1327  					Name: scoreWithNormalizePlugin1,
  1328  					Args: &runtime.Unknown{
  1329  						Raw: []byte(fmt.Sprintf(`{ "normalizeRes": %d }`, framework.MaxNodeScore+1)),
  1330  					},
  1331  				},
  1332  			},
  1333  			err: true,
  1334  		},
  1335  		{
  1336  			name:    "ScoreWithNormalize plugin return score less than MinNodeScore",
  1337  			plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
  1338  			pluginConfigs: []config.PluginConfig{
  1339  				{
  1340  					Name: scoreWithNormalizePlugin1,
  1341  					Args: &runtime.Unknown{
  1342  						Raw: []byte(fmt.Sprintf(`{ "normalizeRes": %d }`, framework.MinNodeScore-1)),
  1343  					},
  1344  				},
  1345  			},
  1346  			err: true,
  1347  		},
  1348  		{
  1349  			name: "single Score plugin with MultiPointExpansion",
  1350  			plugins: &config.Plugins{
  1351  				MultiPoint: config.PluginSet{
  1352  					Enabled: []config.Plugin{
  1353  						{Name: scorePlugin1},
  1354  					},
  1355  				},
  1356  				Score: config.PluginSet{
  1357  					Enabled: []config.Plugin{
  1358  						{Name: scorePlugin1, Weight: 3},
  1359  					},
  1360  				},
  1361  			},
  1362  			pluginConfigs: []config.PluginConfig{
  1363  				{
  1364  					Name: scorePlugin1,
  1365  					Args: &runtime.Unknown{
  1366  						Raw: []byte(`{ "scoreRes": 1 }`),
  1367  					},
  1368  				},
  1369  			},
  1370  			// scorePlugin1 Score returns 1, weight=3, so want=3.
  1371  			want: []framework.NodePluginScores{
  1372  				{
  1373  					Name: "node1",
  1374  					Scores: []framework.PluginScore{
  1375  						{
  1376  							Name:  scorePlugin1,
  1377  							Score: 3,
  1378  						},
  1379  					},
  1380  					TotalScore: 3,
  1381  				},
  1382  				{
  1383  					Name: "node2",
  1384  					Scores: []framework.PluginScore{
  1385  						{
  1386  							Name:  scorePlugin1,
  1387  							Score: 3,
  1388  						},
  1389  					},
  1390  					TotalScore: 3,
  1391  				},
  1392  			},
  1393  		},
  1394  		{
  1395  			name:    "one success plugin, one skip plugin",
  1396  			plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1),
  1397  			pluginConfigs: []config.PluginConfig{
  1398  				{
  1399  					Name: scorePlugin1,
  1400  					Args: &runtime.Unknown{
  1401  						Raw: []byte(`{ "scoreRes": 1 }`),
  1402  					},
  1403  				},
  1404  				{
  1405  					Name: scoreWithNormalizePlugin1,
  1406  					Args: &runtime.Unknown{
  1407  						Raw: []byte(`{ "scoreStatus": 1 }`), // To make sure this plugin isn't called, set error as an injected result.
  1408  					},
  1409  				},
  1410  			},
  1411  			skippedPlugins: sets.New(scoreWithNormalizePlugin1),
  1412  			want: []framework.NodePluginScores{
  1413  				{
  1414  					Name: "node1",
  1415  					Scores: []framework.PluginScore{
  1416  						{
  1417  							Name:  scorePlugin1,
  1418  							Score: 1,
  1419  						},
  1420  					},
  1421  					TotalScore: 1,
  1422  				},
  1423  				{
  1424  					Name: "node2",
  1425  					Scores: []framework.PluginScore{
  1426  						{
  1427  							Name:  scorePlugin1,
  1428  							Score: 1,
  1429  						},
  1430  					},
  1431  					TotalScore: 1,
  1432  				},
  1433  			},
  1434  		},
  1435  		{
  1436  			name:    "all plugins are skipped in prescore",
  1437  			plugins: buildScoreConfigDefaultWeights(scorePlugin1),
  1438  			pluginConfigs: []config.PluginConfig{
  1439  				{
  1440  					Name: scorePlugin1,
  1441  					Args: &runtime.Unknown{
  1442  						Raw: []byte(`{ "scoreStatus": 1 }`), // To make sure this plugin isn't called, set error as an injected result.
  1443  					},
  1444  				},
  1445  			},
  1446  			skippedPlugins: sets.New(scorePlugin1),
  1447  			want: []framework.NodePluginScores{
  1448  				{
  1449  					Name:   "node1",
  1450  					Scores: []framework.PluginScore{},
  1451  				},
  1452  				{
  1453  					Name:   "node2",
  1454  					Scores: []framework.PluginScore{},
  1455  				},
  1456  			},
  1457  		},
  1458  		{
  1459  			name:           "skipped prescore plugin number greater than the number of score plugins",
  1460  			plugins:        buildScoreConfigDefaultWeights(scorePlugin1),
  1461  			pluginConfigs:  nil,
  1462  			skippedPlugins: sets.New(scorePlugin1, "score-plugin-unknown"),
  1463  			want: []framework.NodePluginScores{
  1464  				{
  1465  					Name:   "node1",
  1466  					Scores: []framework.PluginScore{},
  1467  				},
  1468  				{
  1469  					Name:   "node2",
  1470  					Scores: []framework.PluginScore{},
  1471  				},
  1472  			},
  1473  		},
  1474  	}
  1475  
  1476  	for _, tt := range tests {
  1477  		t.Run(tt.name, func(t *testing.T) {
  1478  			// Inject the results via Args in PluginConfig.
  1479  			profile := config.KubeSchedulerProfile{
  1480  				Plugins:      tt.plugins,
  1481  				PluginConfig: tt.pluginConfigs,
  1482  			}
  1483  			ctx, cancel := context.WithCancel(context.Background())
  1484  			defer cancel()
  1485  			f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
  1486  			if err != nil {
  1487  				t.Fatalf("Failed to create framework for testing: %v", err)
  1488  			}
  1489  
  1490  			state := framework.NewCycleState()
  1491  			state.SkipScorePlugins = tt.skippedPlugins
  1492  			res, status := f.RunScorePlugins(ctx, state, pod, BuildNodeInfos(nodes))
  1493  
  1494  			if tt.err {
  1495  				if status.IsSuccess() {
  1496  					t.Errorf("Expected status to be non-success. got: %v", status.Code().String())
  1497  				}
  1498  				return
  1499  			}
  1500  
  1501  			if !status.IsSuccess() {
  1502  				t.Errorf("Expected status to be success.")
  1503  			}
  1504  			if !reflect.DeepEqual(res, tt.want) {
  1505  				t.Errorf("Score map after RunScorePlugin. got: %+v, want: %+v.", res, tt.want)
  1506  			}
  1507  		})
  1508  	}
  1509  }
  1510  
  1511  func TestPreFilterPlugins(t *testing.T) {
  1512  	preFilter1 := &TestPreFilterPlugin{}
  1513  	preFilter2 := &TestPreFilterWithExtensionsPlugin{}
  1514  	r := make(Registry)
  1515  	r.Register(preFilterPluginName,
  1516  		func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
  1517  			return preFilter1, nil
  1518  		})
  1519  	r.Register(preFilterWithExtensionsPluginName,
  1520  		func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
  1521  			return preFilter2, nil
  1522  		})
  1523  	plugins := &config.Plugins{PreFilter: config.PluginSet{Enabled: []config.Plugin{{Name: preFilterWithExtensionsPluginName}, {Name: preFilterPluginName}}}}
  1524  	t.Run("TestPreFilterPlugin", func(t *testing.T) {
  1525  		profile := config.KubeSchedulerProfile{Plugins: plugins}
  1526  		ctx, cancel := context.WithCancel(context.Background())
  1527  		defer cancel()
  1528  
  1529  		f, err := newFrameworkWithQueueSortAndBind(ctx, r, profile)
  1530  		if err != nil {
  1531  			t.Fatalf("Failed to create framework for testing: %v", err)
  1532  		}
  1533  		state := framework.NewCycleState()
  1534  
  1535  		f.RunPreFilterPlugins(ctx, state, nil)
  1536  		f.RunPreFilterExtensionAddPod(ctx, state, nil, nil, nil)
  1537  		f.RunPreFilterExtensionRemovePod(ctx, state, nil, nil, nil)
  1538  
  1539  		if preFilter1.PreFilterCalled != 1 {
  1540  			t.Errorf("preFilter1 called %v, expected: 1", preFilter1.PreFilterCalled)
  1541  		}
  1542  		if preFilter2.PreFilterCalled != 1 {
  1543  			t.Errorf("preFilter2 called %v, expected: 1", preFilter2.PreFilterCalled)
  1544  		}
  1545  		if preFilter2.AddCalled != 1 {
  1546  			t.Errorf("AddPod called %v, expected: 1", preFilter2.AddCalled)
  1547  		}
  1548  		if preFilter2.RemoveCalled != 1 {
  1549  			t.Errorf("AddPod called %v, expected: 1", preFilter2.RemoveCalled)
  1550  		}
  1551  	})
  1552  }
  1553  
  1554  func TestRunPreFilterPlugins(t *testing.T) {
  1555  	tests := []struct {
  1556  		name                string
  1557  		plugins             []*TestPlugin
  1558  		wantPreFilterResult *framework.PreFilterResult
  1559  		wantSkippedPlugins  sets.Set[string]
  1560  		wantStatusCode      framework.Code
  1561  	}{
  1562  		{
  1563  			name: "all PreFilter returned success",
  1564  			plugins: []*TestPlugin{
  1565  				{
  1566  					name: "success1",
  1567  				},
  1568  				{
  1569  					name: "success2",
  1570  				},
  1571  			},
  1572  			wantPreFilterResult: nil,
  1573  			wantStatusCode:      framework.Success,
  1574  		},
  1575  		{
  1576  			name: "one PreFilter plugin returned success, but another PreFilter plugin returned non-success",
  1577  			plugins: []*TestPlugin{
  1578  				{
  1579  					name: "success",
  1580  				},
  1581  				{
  1582  					name: "error",
  1583  					inj:  injectedResult{PreFilterStatus: int(framework.Error)},
  1584  				},
  1585  			},
  1586  			wantPreFilterResult: nil,
  1587  			wantStatusCode:      framework.Error,
  1588  		},
  1589  		{
  1590  			name: "one PreFilter plugin returned skip, but another PreFilter plugin returned non-success",
  1591  			plugins: []*TestPlugin{
  1592  				{
  1593  					name: "skip",
  1594  					inj:  injectedResult{PreFilterStatus: int(framework.Skip)},
  1595  				},
  1596  				{
  1597  					name: "error",
  1598  					inj:  injectedResult{PreFilterStatus: int(framework.Error)},
  1599  				},
  1600  			},
  1601  			wantSkippedPlugins: sets.New("skip"),
  1602  			wantStatusCode:     framework.Error,
  1603  		},
  1604  		{
  1605  			name: "all PreFilter plugins returned skip",
  1606  			plugins: []*TestPlugin{
  1607  				{
  1608  					name: "skip1",
  1609  					inj:  injectedResult{PreFilterStatus: int(framework.Skip)},
  1610  				},
  1611  				{
  1612  					name: "skip2",
  1613  					inj:  injectedResult{PreFilterStatus: int(framework.Skip)},
  1614  				},
  1615  				{
  1616  					name: "skip3",
  1617  					inj:  injectedResult{PreFilterStatus: int(framework.Skip)},
  1618  				},
  1619  			},
  1620  			wantPreFilterResult: nil,
  1621  			wantSkippedPlugins:  sets.New("skip1", "skip2", "skip3"),
  1622  			wantStatusCode:      framework.Success,
  1623  		},
  1624  		{
  1625  			name: "some PreFilter plugins returned skip",
  1626  			plugins: []*TestPlugin{
  1627  				{
  1628  					name: "skip1",
  1629  					inj:  injectedResult{PreFilterStatus: int(framework.Skip)},
  1630  				},
  1631  				{
  1632  					name: "success1",
  1633  				},
  1634  				{
  1635  					name: "skip2",
  1636  					inj:  injectedResult{PreFilterStatus: int(framework.Skip)},
  1637  				},
  1638  				{
  1639  					name: "success2",
  1640  				},
  1641  			},
  1642  			wantPreFilterResult: nil,
  1643  			wantSkippedPlugins:  sets.New("skip1", "skip2"),
  1644  			wantStatusCode:      framework.Success,
  1645  		},
  1646  		{
  1647  			name: "one PreFilter plugin returned Unschedulable, but another PreFilter plugin should be executed",
  1648  			plugins: []*TestPlugin{
  1649  				{
  1650  					name: "unschedulable",
  1651  					inj:  injectedResult{PreFilterStatus: int(framework.Unschedulable)},
  1652  				},
  1653  				{
  1654  					// to make sure this plugin is executed, this plugin return Skip and we confirm it via wantSkippedPlugins.
  1655  					name: "skip",
  1656  					inj:  injectedResult{PreFilterStatus: int(framework.Skip)},
  1657  				},
  1658  			},
  1659  			wantPreFilterResult: nil,
  1660  			wantSkippedPlugins:  sets.New("skip"),
  1661  			wantStatusCode:      framework.Unschedulable,
  1662  		},
  1663  		{
  1664  			name: "one PreFilter plugin returned UnschedulableAndUnresolvable, and all other plugins aren't executed",
  1665  			plugins: []*TestPlugin{
  1666  				{
  1667  					name: "unresolvable",
  1668  					inj:  injectedResult{PreFilterStatus: int(framework.UnschedulableAndUnresolvable)},
  1669  				},
  1670  				{
  1671  					// to make sure this plugin is not executed, this plugin return Skip and we confirm it via wantSkippedPlugins.
  1672  					name: "skip",
  1673  					inj:  injectedResult{PreFilterStatus: int(framework.Skip)},
  1674  				},
  1675  			},
  1676  			wantPreFilterResult: nil,
  1677  			wantStatusCode:      framework.UnschedulableAndUnresolvable,
  1678  		},
  1679  		{
  1680  			name: "all nodes are filtered out by prefilter result, but other plugins aren't executed because we consider all nodes are filtered out by UnschedulableAndUnresolvable",
  1681  			plugins: []*TestPlugin{
  1682  				{
  1683  					name: "reject-all-nodes",
  1684  					inj:  injectedResult{PreFilterResult: &framework.PreFilterResult{NodeNames: sets.New[string]()}},
  1685  				},
  1686  				{
  1687  					// to make sure this plugin is not executed, this plugin return Skip and we confirm it via wantSkippedPlugins.
  1688  					name: "skip",
  1689  					inj:  injectedResult{PreFilterStatus: int(framework.Skip)},
  1690  				},
  1691  			},
  1692  			wantPreFilterResult: &framework.PreFilterResult{NodeNames: sets.New[string]()},
  1693  			wantSkippedPlugins:  sets.New[string](), // "skip" plugin isn't executed.
  1694  			wantStatusCode:      framework.UnschedulableAndUnresolvable,
  1695  		},
  1696  	}
  1697  	for _, tt := range tests {
  1698  		t.Run(tt.name, func(t *testing.T) {
  1699  			r := make(Registry)
  1700  			enabled := make([]config.Plugin, len(tt.plugins))
  1701  			for i, p := range tt.plugins {
  1702  				p := p
  1703  				enabled[i].Name = p.name
  1704  				if err := r.Register(p.name, func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
  1705  					return p, nil
  1706  				}); err != nil {
  1707  					t.Fatalf("fail to register PreFilter plugin (%s)", p.Name())
  1708  				}
  1709  			}
  1710  
  1711  			ctx, cancel := context.WithCancel(context.Background())
  1712  			defer cancel()
  1713  
  1714  			f, err := newFrameworkWithQueueSortAndBind(
  1715  				ctx,
  1716  				r,
  1717  				config.KubeSchedulerProfile{Plugins: &config.Plugins{PreFilter: config.PluginSet{Enabled: enabled}}},
  1718  			)
  1719  			if err != nil {
  1720  				t.Fatalf("Failed to create framework for testing: %v", err)
  1721  			}
  1722  
  1723  			state := framework.NewCycleState()
  1724  			result, status := f.RunPreFilterPlugins(ctx, state, nil)
  1725  			if d := cmp.Diff(result, tt.wantPreFilterResult); d != "" {
  1726  				t.Errorf("wrong status. got: %v, want: %v, diff: %s", result, tt.wantPreFilterResult, d)
  1727  			}
  1728  			if status.Code() != tt.wantStatusCode {
  1729  				t.Errorf("wrong status code. got: %v, want: %v", status, tt.wantStatusCode)
  1730  			}
  1731  			skipped := state.SkipFilterPlugins
  1732  			if d := cmp.Diff(skipped, tt.wantSkippedPlugins); d != "" {
  1733  				t.Errorf("wrong skip filter plugins. got: %v, want: %v, diff: %s", skipped, tt.wantSkippedPlugins, d)
  1734  			}
  1735  		})
  1736  	}
  1737  }
  1738  
  1739  func TestRunPreFilterExtensionRemovePod(t *testing.T) {
  1740  	tests := []struct {
  1741  		name               string
  1742  		plugins            []*TestPlugin
  1743  		skippedPluginNames sets.Set[string]
  1744  		wantStatusCode     framework.Code
  1745  	}{
  1746  		{
  1747  			name: "no plugins are skipped and all RemovePod() returned success",
  1748  			plugins: []*TestPlugin{
  1749  				{
  1750  					name: "success1",
  1751  				},
  1752  				{
  1753  					name: "success2",
  1754  				},
  1755  			},
  1756  			wantStatusCode: framework.Success,
  1757  		},
  1758  		{
  1759  			name: "one RemovePod() returned error",
  1760  			plugins: []*TestPlugin{
  1761  				{
  1762  					name: "success1",
  1763  				},
  1764  				{
  1765  					name: "error1",
  1766  					inj:  injectedResult{PreFilterRemovePodStatus: int(framework.Error)},
  1767  				},
  1768  			},
  1769  			wantStatusCode: framework.Error,
  1770  		},
  1771  		{
  1772  			name: "one RemovePod() is skipped",
  1773  			plugins: []*TestPlugin{
  1774  				{
  1775  					name: "success1",
  1776  				},
  1777  				{
  1778  					name: "skipped",
  1779  					// To confirm it's skipped, return error so that this test case will fail when it isn't skipped.
  1780  					inj: injectedResult{PreFilterRemovePodStatus: int(framework.Error)},
  1781  				},
  1782  			},
  1783  			skippedPluginNames: sets.New("skipped"),
  1784  			wantStatusCode:     framework.Success,
  1785  		},
  1786  	}
  1787  	for _, tt := range tests {
  1788  		t.Run(tt.name, func(t *testing.T) {
  1789  			r := make(Registry)
  1790  			enabled := make([]config.Plugin, len(tt.plugins))
  1791  			for i, p := range tt.plugins {
  1792  				p := p
  1793  				enabled[i].Name = p.name
  1794  				if err := r.Register(p.name, func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
  1795  					return p, nil
  1796  				}); err != nil {
  1797  					t.Fatalf("fail to register PreFilterExtension plugin (%s)", p.Name())
  1798  				}
  1799  			}
  1800  
  1801  			ctx, cancel := context.WithCancel(context.Background())
  1802  			defer cancel()
  1803  
  1804  			f, err := newFrameworkWithQueueSortAndBind(
  1805  				ctx,
  1806  				r,
  1807  				config.KubeSchedulerProfile{Plugins: &config.Plugins{PreFilter: config.PluginSet{Enabled: enabled}}},
  1808  			)
  1809  			if err != nil {
  1810  				t.Fatalf("Failed to create framework for testing: %v", err)
  1811  			}
  1812  
  1813  			state := framework.NewCycleState()
  1814  			state.SkipFilterPlugins = tt.skippedPluginNames
  1815  			status := f.RunPreFilterExtensionRemovePod(ctx, state, nil, nil, nil)
  1816  			if status.Code() != tt.wantStatusCode {
  1817  				t.Errorf("wrong status code. got: %v, want: %v", status, tt.wantStatusCode)
  1818  			}
  1819  		})
  1820  	}
  1821  }
  1822  
  1823  func TestRunPreFilterExtensionAddPod(t *testing.T) {
  1824  	tests := []struct {
  1825  		name               string
  1826  		plugins            []*TestPlugin
  1827  		skippedPluginNames sets.Set[string]
  1828  		wantStatusCode     framework.Code
  1829  	}{
  1830  		{
  1831  			name: "no plugins are skipped and all AddPod() returned success",
  1832  			plugins: []*TestPlugin{
  1833  				{
  1834  					name: "success1",
  1835  				},
  1836  				{
  1837  					name: "success2",
  1838  				},
  1839  			},
  1840  			wantStatusCode: framework.Success,
  1841  		},
  1842  		{
  1843  			name: "one AddPod() returned error",
  1844  			plugins: []*TestPlugin{
  1845  				{
  1846  					name: "success1",
  1847  				},
  1848  				{
  1849  					name: "error1",
  1850  					inj:  injectedResult{PreFilterAddPodStatus: int(framework.Error)},
  1851  				},
  1852  			},
  1853  			wantStatusCode: framework.Error,
  1854  		},
  1855  		{
  1856  			name: "one AddPod() is skipped",
  1857  			plugins: []*TestPlugin{
  1858  				{
  1859  					name: "success1",
  1860  				},
  1861  				{
  1862  					name: "skipped",
  1863  					// To confirm it's skipped, return error so that this test case will fail when it isn't skipped.
  1864  					inj: injectedResult{PreFilterAddPodStatus: int(framework.Error)},
  1865  				},
  1866  			},
  1867  			skippedPluginNames: sets.New("skipped"),
  1868  			wantStatusCode:     framework.Success,
  1869  		},
  1870  	}
  1871  	for _, tt := range tests {
  1872  		t.Run(tt.name, func(t *testing.T) {
  1873  			r := make(Registry)
  1874  			enabled := make([]config.Plugin, len(tt.plugins))
  1875  			for i, p := range tt.plugins {
  1876  				p := p
  1877  				enabled[i].Name = p.name
  1878  				if err := r.Register(p.name, func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
  1879  					return p, nil
  1880  				}); err != nil {
  1881  					t.Fatalf("fail to register PreFilterExtension plugin (%s)", p.Name())
  1882  				}
  1883  			}
  1884  
  1885  			ctx, cancel := context.WithCancel(context.Background())
  1886  			defer cancel()
  1887  
  1888  			f, err := newFrameworkWithQueueSortAndBind(
  1889  				ctx,
  1890  				r,
  1891  				config.KubeSchedulerProfile{Plugins: &config.Plugins{PreFilter: config.PluginSet{Enabled: enabled}}},
  1892  			)
  1893  			if err != nil {
  1894  				t.Fatalf("Failed to create framework for testing: %v", err)
  1895  			}
  1896  
  1897  			state := framework.NewCycleState()
  1898  			state.SkipFilterPlugins = tt.skippedPluginNames
  1899  			status := f.RunPreFilterExtensionAddPod(ctx, state, nil, nil, nil)
  1900  			if status.Code() != tt.wantStatusCode {
  1901  				t.Errorf("wrong status code. got: %v, want: %v", status, tt.wantStatusCode)
  1902  			}
  1903  		})
  1904  	}
  1905  }
  1906  
  1907  func TestFilterPlugins(t *testing.T) {
  1908  	tests := []struct {
  1909  		name           string
  1910  		plugins        []*TestPlugin
  1911  		skippedPlugins sets.Set[string]
  1912  		wantStatus     *framework.Status
  1913  	}{
  1914  		{
  1915  			name: "SuccessFilter",
  1916  			plugins: []*TestPlugin{
  1917  				{
  1918  					name: "TestPlugin",
  1919  					inj:  injectedResult{FilterStatus: int(framework.Success)},
  1920  				},
  1921  			},
  1922  			wantStatus: nil,
  1923  		},
  1924  		{
  1925  			name: "ErrorFilter",
  1926  			plugins: []*TestPlugin{
  1927  				{
  1928  					name: "TestPlugin",
  1929  					inj:  injectedResult{FilterStatus: int(framework.Error)},
  1930  				},
  1931  			},
  1932  			wantStatus: framework.AsStatus(fmt.Errorf(`running "TestPlugin" filter plugin: %w`, errInjectedFilterStatus)).WithPlugin("TestPlugin"),
  1933  		},
  1934  		{
  1935  			name: "UnschedulableFilter",
  1936  			plugins: []*TestPlugin{
  1937  				{
  1938  					name: "TestPlugin",
  1939  					inj:  injectedResult{FilterStatus: int(framework.Unschedulable)},
  1940  				},
  1941  			},
  1942  			wantStatus: framework.NewStatus(framework.Unschedulable, injectFilterReason).WithPlugin("TestPlugin"),
  1943  		},
  1944  		{
  1945  			name: "UnschedulableAndUnresolvableFilter",
  1946  			plugins: []*TestPlugin{
  1947  				{
  1948  					name: "TestPlugin",
  1949  					inj: injectedResult{
  1950  						FilterStatus: int(framework.UnschedulableAndUnresolvable)},
  1951  				},
  1952  			},
  1953  			wantStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, injectFilterReason).WithPlugin("TestPlugin"),
  1954  		},
  1955  		// following tests cover multiple-plugins scenarios
  1956  		{
  1957  			name: "ErrorAndErrorFilters",
  1958  			plugins: []*TestPlugin{
  1959  				{
  1960  					name: "TestPlugin1",
  1961  					inj:  injectedResult{FilterStatus: int(framework.Error)},
  1962  				},
  1963  				{
  1964  					name: "TestPlugin2",
  1965  					inj:  injectedResult{FilterStatus: int(framework.Error)},
  1966  				},
  1967  			},
  1968  			wantStatus: framework.AsStatus(fmt.Errorf(`running "TestPlugin1" filter plugin: %w`, errInjectedFilterStatus)).WithPlugin("TestPlugin1"),
  1969  		},
  1970  		{
  1971  			name: "UnschedulableAndUnschedulableFilters",
  1972  			plugins: []*TestPlugin{
  1973  				{
  1974  					name: "TestPlugin1",
  1975  					inj:  injectedResult{FilterStatus: int(framework.Unschedulable)},
  1976  				},
  1977  				{
  1978  					name: "TestPlugin2",
  1979  					inj:  injectedResult{FilterStatus: int(framework.Unschedulable)},
  1980  				},
  1981  			},
  1982  			wantStatus: framework.NewStatus(framework.Unschedulable, injectFilterReason).WithPlugin("TestPlugin1"),
  1983  		},
  1984  		{
  1985  			name: "UnschedulableAndUnschedulableAndUnresolvableFilters",
  1986  			plugins: []*TestPlugin{
  1987  				{
  1988  					name: "TestPlugin1",
  1989  					inj:  injectedResult{FilterStatus: int(framework.UnschedulableAndUnresolvable)},
  1990  				},
  1991  				{
  1992  					name: "TestPlugin2",
  1993  					inj:  injectedResult{FilterStatus: int(framework.Unschedulable)},
  1994  				},
  1995  			},
  1996  			wantStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, injectFilterReason).WithPlugin("TestPlugin1"),
  1997  		},
  1998  		{
  1999  			name: "SuccessAndSuccessFilters",
  2000  			plugins: []*TestPlugin{
  2001  				{
  2002  					name: "TestPlugin1",
  2003  					inj:  injectedResult{FilterStatus: int(framework.Success)},
  2004  				},
  2005  				{
  2006  					name: "TestPlugin2",
  2007  					inj:  injectedResult{FilterStatus: int(framework.Success)},
  2008  				},
  2009  			},
  2010  			wantStatus: nil,
  2011  		},
  2012  		{
  2013  			name: "SuccessAndSkipFilters",
  2014  			plugins: []*TestPlugin{
  2015  				{
  2016  					name: "TestPlugin1",
  2017  					inj:  injectedResult{FilterStatus: int(framework.Success)},
  2018  				},
  2019  
  2020  				{
  2021  					name: "TestPlugin2",
  2022  					inj:  injectedResult{FilterStatus: int(framework.Error)}, // To make sure this plugins isn't called, set error as an injected result.
  2023  				},
  2024  			},
  2025  			wantStatus:     nil,
  2026  			skippedPlugins: sets.New("TestPlugin2"),
  2027  		},
  2028  		{
  2029  			name: "ErrorAndSuccessFilters",
  2030  			plugins: []*TestPlugin{
  2031  				{
  2032  					name: "TestPlugin1",
  2033  					inj:  injectedResult{FilterStatus: int(framework.Error)},
  2034  				},
  2035  				{
  2036  					name: "TestPlugin2",
  2037  					inj:  injectedResult{FilterStatus: int(framework.Success)},
  2038  				},
  2039  			},
  2040  			wantStatus: framework.AsStatus(fmt.Errorf(`running "TestPlugin1" filter plugin: %w`, errInjectedFilterStatus)).WithPlugin("TestPlugin1"),
  2041  		},
  2042  		{
  2043  			name: "SuccessAndErrorFilters",
  2044  			plugins: []*TestPlugin{
  2045  				{
  2046  
  2047  					name: "TestPlugin1",
  2048  					inj:  injectedResult{FilterStatus: int(framework.Success)},
  2049  				},
  2050  				{
  2051  					name: "TestPlugin2",
  2052  					inj:  injectedResult{FilterStatus: int(framework.Error)},
  2053  				},
  2054  			},
  2055  			wantStatus: framework.AsStatus(fmt.Errorf(`running "TestPlugin2" filter plugin: %w`, errInjectedFilterStatus)).WithPlugin("TestPlugin2"),
  2056  		},
  2057  		{
  2058  			name: "SuccessAndUnschedulableFilters",
  2059  			plugins: []*TestPlugin{
  2060  				{
  2061  					name: "TestPlugin1",
  2062  					inj:  injectedResult{FilterStatus: int(framework.Success)},
  2063  				},
  2064  				{
  2065  					name: "TestPlugin2",
  2066  					inj:  injectedResult{FilterStatus: int(framework.Unschedulable)},
  2067  				},
  2068  			},
  2069  			wantStatus: framework.NewStatus(framework.Unschedulable, injectFilterReason).WithPlugin("TestPlugin2"),
  2070  		},
  2071  	}
  2072  
  2073  	for _, tt := range tests {
  2074  		t.Run(tt.name, func(t *testing.T) {
  2075  			registry := Registry{}
  2076  			cfgPls := &config.Plugins{}
  2077  			for _, pl := range tt.plugins {
  2078  				// register all plugins
  2079  				tmpPl := pl
  2080  				if err := registry.Register(pl.name,
  2081  					func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
  2082  						return tmpPl, nil
  2083  					}); err != nil {
  2084  					t.Fatalf("fail to register filter plugin (%s)", pl.name)
  2085  				}
  2086  				// append plugins to filter pluginset
  2087  				cfgPls.Filter.Enabled = append(
  2088  					cfgPls.Filter.Enabled,
  2089  					config.Plugin{Name: pl.name})
  2090  			}
  2091  			profile := config.KubeSchedulerProfile{Plugins: cfgPls}
  2092  			ctx, cancel := context.WithCancel(context.Background())
  2093  			defer cancel()
  2094  
  2095  			f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
  2096  			if err != nil {
  2097  				t.Fatalf("fail to create framework: %s", err)
  2098  			}
  2099  			state := framework.NewCycleState()
  2100  			state.SkipFilterPlugins = tt.skippedPlugins
  2101  			gotStatus := f.RunFilterPlugins(ctx, state, pod, nil)
  2102  			if diff := cmp.Diff(gotStatus, tt.wantStatus, cmpOpts...); diff != "" {
  2103  				t.Errorf("Unexpected status: (-got, +want):\n%s", diff)
  2104  			}
  2105  		})
  2106  	}
  2107  }
  2108  
  2109  func TestPostFilterPlugins(t *testing.T) {
  2110  	tests := []struct {
  2111  		name       string
  2112  		plugins    []*TestPlugin
  2113  		wantStatus *framework.Status
  2114  	}{
  2115  		{
  2116  			name: "a single plugin makes a Pod schedulable",
  2117  			plugins: []*TestPlugin{
  2118  				{
  2119  					name: "TestPlugin",
  2120  					inj:  injectedResult{PostFilterStatus: int(framework.Success)},
  2121  				},
  2122  			},
  2123  			wantStatus: framework.NewStatus(framework.Success, injectReason),
  2124  		},
  2125  		{
  2126  			name: "plugin1 failed to make a Pod schedulable, followed by plugin2 which makes the Pod schedulable",
  2127  			plugins: []*TestPlugin{
  2128  				{
  2129  					name: "TestPlugin1",
  2130  					inj:  injectedResult{PostFilterStatus: int(framework.Unschedulable)},
  2131  				},
  2132  				{
  2133  					name: "TestPlugin2",
  2134  					inj:  injectedResult{PostFilterStatus: int(framework.Success)},
  2135  				},
  2136  			},
  2137  			wantStatus: framework.NewStatus(framework.Success, injectReason),
  2138  		},
  2139  		{
  2140  			name: "plugin1 makes a Pod schedulable, followed by plugin2 which cannot make the Pod schedulable",
  2141  			plugins: []*TestPlugin{
  2142  				{
  2143  					name: "TestPlugin1",
  2144  					inj:  injectedResult{PostFilterStatus: int(framework.Success)},
  2145  				},
  2146  				{
  2147  					name: "TestPlugin2",
  2148  					inj:  injectedResult{PostFilterStatus: int(framework.Unschedulable)},
  2149  				},
  2150  			},
  2151  			wantStatus: framework.NewStatus(framework.Success, injectReason),
  2152  		},
  2153  		{
  2154  			name: "plugin1 failed to make a Pod schedulable, followed by plugin2 which makes the Pod schedulable",
  2155  			plugins: []*TestPlugin{
  2156  				{
  2157  					name: "TestPlugin1",
  2158  					inj:  injectedResult{PostFilterStatus: int(framework.Error)},
  2159  				},
  2160  				{
  2161  					name: "TestPlugin2",
  2162  					inj:  injectedResult{PostFilterStatus: int(framework.Success)},
  2163  				},
  2164  			},
  2165  			wantStatus: framework.AsStatus(fmt.Errorf(injectReason)).WithPlugin("TestPlugin1"),
  2166  		},
  2167  		{
  2168  			name: "plugin1 failed to make a Pod schedulable, followed by plugin2 which makes the Pod unresolvable",
  2169  			plugins: []*TestPlugin{
  2170  				{
  2171  					name: "TestPlugin1",
  2172  					inj:  injectedResult{PostFilterStatus: int(framework.Unschedulable)},
  2173  				},
  2174  				{
  2175  					name: "TestPlugin2",
  2176  					inj:  injectedResult{PostFilterStatus: int(framework.UnschedulableAndUnresolvable)},
  2177  				},
  2178  			},
  2179  			wantStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, injectReason).WithPlugin("TestPlugin2"),
  2180  		},
  2181  		{
  2182  			name: "both plugins failed to make a Pod schedulable",
  2183  			plugins: []*TestPlugin{
  2184  				{
  2185  					name: "TestPlugin1",
  2186  					inj:  injectedResult{PostFilterStatus: int(framework.Unschedulable)},
  2187  				},
  2188  				{
  2189  					name: "TestPlugin2",
  2190  					inj:  injectedResult{PostFilterStatus: int(framework.Unschedulable)},
  2191  				},
  2192  			},
  2193  			wantStatus: framework.NewStatus(framework.Unschedulable, []string{injectReason, injectReason}...).WithPlugin("TestPlugin1"),
  2194  		},
  2195  	}
  2196  
  2197  	for _, tt := range tests {
  2198  		t.Run(tt.name, func(t *testing.T) {
  2199  			registry := Registry{}
  2200  			cfgPls := &config.Plugins{}
  2201  			for _, pl := range tt.plugins {
  2202  				// register all plugins
  2203  				tmpPl := pl
  2204  				if err := registry.Register(pl.name,
  2205  					func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
  2206  						return tmpPl, nil
  2207  					}); err != nil {
  2208  					t.Fatalf("fail to register postFilter plugin (%s)", pl.name)
  2209  				}
  2210  				// append plugins to filter pluginset
  2211  				cfgPls.PostFilter.Enabled = append(
  2212  					cfgPls.PostFilter.Enabled,
  2213  					config.Plugin{Name: pl.name},
  2214  				)
  2215  			}
  2216  			profile := config.KubeSchedulerProfile{Plugins: cfgPls}
  2217  			ctx, cancel := context.WithCancel(context.Background())
  2218  			defer cancel()
  2219  			f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
  2220  			if err != nil {
  2221  				t.Fatalf("fail to create framework: %s", err)
  2222  			}
  2223  			_, gotStatus := f.RunPostFilterPlugins(ctx, nil, pod, nil)
  2224  			if !reflect.DeepEqual(gotStatus, tt.wantStatus) {
  2225  				t.Errorf("Unexpected status. got: %v, want: %v", gotStatus, tt.wantStatus)
  2226  			}
  2227  		})
  2228  	}
  2229  }
  2230  
  2231  func TestFilterPluginsWithNominatedPods(t *testing.T) {
  2232  	tests := []struct {
  2233  		name            string
  2234  		preFilterPlugin *TestPlugin
  2235  		filterPlugin    *TestPlugin
  2236  		pod             *v1.Pod
  2237  		nominatedPod    *v1.Pod
  2238  		node            *v1.Node
  2239  		nodeInfo        *framework.NodeInfo
  2240  		wantStatus      *framework.Status
  2241  	}{
  2242  		{
  2243  			name:            "node has no nominated pod",
  2244  			preFilterPlugin: nil,
  2245  			filterPlugin:    nil,
  2246  			pod:             lowPriorityPod,
  2247  			nominatedPod:    nil,
  2248  			node:            node,
  2249  			nodeInfo:        framework.NewNodeInfo(pod),
  2250  			wantStatus:      nil,
  2251  		},
  2252  		{
  2253  			name: "node has a high-priority nominated pod and all filters succeed",
  2254  			preFilterPlugin: &TestPlugin{
  2255  				name: "TestPlugin1",
  2256  				inj: injectedResult{
  2257  					PreFilterAddPodStatus: int(framework.Success),
  2258  				},
  2259  			},
  2260  			filterPlugin: &TestPlugin{
  2261  				name: "TestPlugin2",
  2262  				inj: injectedResult{
  2263  					FilterStatus: int(framework.Success),
  2264  				},
  2265  			},
  2266  			pod:          lowPriorityPod,
  2267  			nominatedPod: highPriorityPod,
  2268  			node:         node,
  2269  			nodeInfo:     framework.NewNodeInfo(pod),
  2270  			wantStatus:   nil,
  2271  		},
  2272  		{
  2273  			name: "node has a high-priority nominated pod and pre filters fail",
  2274  			preFilterPlugin: &TestPlugin{
  2275  				name: "TestPlugin1",
  2276  				inj: injectedResult{
  2277  					PreFilterAddPodStatus: int(framework.Error),
  2278  				},
  2279  			},
  2280  			filterPlugin: nil,
  2281  			pod:          lowPriorityPod,
  2282  			nominatedPod: highPriorityPod,
  2283  			node:         node,
  2284  			nodeInfo:     framework.NewNodeInfo(pod),
  2285  			wantStatus:   framework.AsStatus(fmt.Errorf(`running AddPod on PreFilter plugin "TestPlugin1": %w`, errInjectedStatus)),
  2286  		},
  2287  		{
  2288  			name: "node has a high-priority nominated pod and filters fail",
  2289  			preFilterPlugin: &TestPlugin{
  2290  				name: "TestPlugin1",
  2291  				inj: injectedResult{
  2292  					PreFilterAddPodStatus: int(framework.Success),
  2293  				},
  2294  			},
  2295  			filterPlugin: &TestPlugin{
  2296  				name: "TestPlugin2",
  2297  				inj: injectedResult{
  2298  					FilterStatus: int(framework.Error),
  2299  				},
  2300  			},
  2301  			pod:          lowPriorityPod,
  2302  			nominatedPod: highPriorityPod,
  2303  			node:         node,
  2304  			nodeInfo:     framework.NewNodeInfo(pod),
  2305  			wantStatus:   framework.AsStatus(fmt.Errorf(`running "TestPlugin2" filter plugin: %w`, errInjectedFilterStatus)).WithPlugin("TestPlugin2"),
  2306  		},
  2307  		{
  2308  			name: "node has a low-priority nominated pod and pre filters return unschedulable",
  2309  			preFilterPlugin: &TestPlugin{
  2310  				name: "TestPlugin1",
  2311  				inj: injectedResult{
  2312  					PreFilterAddPodStatus: int(framework.Unschedulable),
  2313  				},
  2314  			},
  2315  			filterPlugin: &TestPlugin{
  2316  				name: "TestPlugin2",
  2317  				inj: injectedResult{
  2318  					FilterStatus: int(framework.Success),
  2319  				},
  2320  			},
  2321  			pod:          highPriorityPod,
  2322  			nominatedPod: lowPriorityPod,
  2323  			node:         node,
  2324  			nodeInfo:     framework.NewNodeInfo(pod),
  2325  			wantStatus:   nil,
  2326  		},
  2327  	}
  2328  
  2329  	for _, tt := range tests {
  2330  		t.Run(tt.name, func(t *testing.T) {
  2331  			logger, _ := ktesting.NewTestContext(t)
  2332  			registry := Registry{}
  2333  			cfgPls := &config.Plugins{}
  2334  
  2335  			if tt.preFilterPlugin != nil {
  2336  				if err := registry.Register(tt.preFilterPlugin.name,
  2337  					func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
  2338  						return tt.preFilterPlugin, nil
  2339  					}); err != nil {
  2340  					t.Fatalf("fail to register preFilter plugin (%s)", tt.preFilterPlugin.name)
  2341  				}
  2342  				cfgPls.PreFilter.Enabled = append(
  2343  					cfgPls.PreFilter.Enabled,
  2344  					config.Plugin{Name: tt.preFilterPlugin.name},
  2345  				)
  2346  			}
  2347  			if tt.filterPlugin != nil {
  2348  				if err := registry.Register(tt.filterPlugin.name,
  2349  					func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
  2350  						return tt.filterPlugin, nil
  2351  					}); err != nil {
  2352  					t.Fatalf("fail to register filter plugin (%s)", tt.filterPlugin.name)
  2353  				}
  2354  				cfgPls.Filter.Enabled = append(
  2355  					cfgPls.Filter.Enabled,
  2356  					config.Plugin{Name: tt.filterPlugin.name},
  2357  				)
  2358  			}
  2359  
  2360  			podNominator := internalqueue.NewPodNominator(nil)
  2361  			if tt.nominatedPod != nil {
  2362  				podNominator.AddNominatedPod(
  2363  					logger,
  2364  					mustNewPodInfo(t, tt.nominatedPod),
  2365  					&framework.NominatingInfo{NominatingMode: framework.ModeOverride, NominatedNodeName: nodeName})
  2366  			}
  2367  			profile := config.KubeSchedulerProfile{Plugins: cfgPls}
  2368  			ctx, cancel := context.WithCancel(context.Background())
  2369  			defer cancel()
  2370  			f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile, WithPodNominator(podNominator))
  2371  			if err != nil {
  2372  				t.Fatalf("fail to create framework: %s", err)
  2373  			}
  2374  			tt.nodeInfo.SetNode(tt.node)
  2375  			gotStatus := f.RunFilterPluginsWithNominatedPods(ctx, framework.NewCycleState(), tt.pod, tt.nodeInfo)
  2376  			if diff := cmp.Diff(gotStatus, tt.wantStatus, cmpOpts...); diff != "" {
  2377  				t.Errorf("Unexpected status: (-got, +want):\n%s", diff)
  2378  			}
  2379  		})
  2380  	}
  2381  }
  2382  
  2383  func TestPreBindPlugins(t *testing.T) {
  2384  	tests := []struct {
  2385  		name       string
  2386  		plugins    []*TestPlugin
  2387  		wantStatus *framework.Status
  2388  	}{
  2389  		{
  2390  			name:       "NoPreBindPlugin",
  2391  			plugins:    []*TestPlugin{},
  2392  			wantStatus: nil,
  2393  		},
  2394  		{
  2395  			name: "SuccessPreBindPlugins",
  2396  			plugins: []*TestPlugin{
  2397  				{
  2398  					name: "TestPlugin",
  2399  					inj:  injectedResult{PreBindStatus: int(framework.Success)},
  2400  				},
  2401  			},
  2402  			wantStatus: nil,
  2403  		},
  2404  		{
  2405  			name: "UnschedulablePreBindPlugin",
  2406  			plugins: []*TestPlugin{
  2407  				{
  2408  					name: "TestPlugin",
  2409  					inj:  injectedResult{PreBindStatus: int(framework.Unschedulable)},
  2410  				},
  2411  			},
  2412  			wantStatus: framework.NewStatus(framework.Unschedulable, injectReason).WithPlugin("TestPlugin"),
  2413  		},
  2414  		{
  2415  			name: "ErrorPreBindPlugin",
  2416  			plugins: []*TestPlugin{
  2417  				{
  2418  					name: "TestPlugin",
  2419  					inj:  injectedResult{PreBindStatus: int(framework.Error)},
  2420  				},
  2421  			},
  2422  			wantStatus: framework.AsStatus(fmt.Errorf(`running PreBind plugin "TestPlugin": %w`, errInjectedStatus)),
  2423  		},
  2424  		{
  2425  			name: "UnschedulablePreBindPlugin",
  2426  			plugins: []*TestPlugin{
  2427  				{
  2428  					name: "TestPlugin",
  2429  					inj:  injectedResult{PreBindStatus: int(framework.UnschedulableAndUnresolvable)},
  2430  				},
  2431  			},
  2432  			wantStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, injectReason).WithPlugin("TestPlugin"),
  2433  		},
  2434  		{
  2435  			name: "SuccessErrorPreBindPlugins",
  2436  			plugins: []*TestPlugin{
  2437  				{
  2438  					name: "TestPlugin",
  2439  					inj:  injectedResult{PreBindStatus: int(framework.Success)},
  2440  				},
  2441  				{
  2442  					name: "TestPlugin 1",
  2443  					inj:  injectedResult{PreBindStatus: int(framework.Error)},
  2444  				},
  2445  			},
  2446  			wantStatus: framework.AsStatus(fmt.Errorf(`running PreBind plugin "TestPlugin 1": %w`, errInjectedStatus)),
  2447  		},
  2448  		{
  2449  			name: "ErrorSuccessPreBindPlugin",
  2450  			plugins: []*TestPlugin{
  2451  				{
  2452  					name: "TestPlugin",
  2453  					inj:  injectedResult{PreBindStatus: int(framework.Error)},
  2454  				},
  2455  				{
  2456  					name: "TestPlugin 1",
  2457  					inj:  injectedResult{PreBindStatus: int(framework.Success)},
  2458  				},
  2459  			},
  2460  			wantStatus: framework.AsStatus(fmt.Errorf(`running PreBind plugin "TestPlugin": %w`, errInjectedStatus)),
  2461  		},
  2462  		{
  2463  			name: "SuccessSuccessPreBindPlugin",
  2464  			plugins: []*TestPlugin{
  2465  				{
  2466  					name: "TestPlugin",
  2467  					inj:  injectedResult{PreBindStatus: int(framework.Success)},
  2468  				},
  2469  				{
  2470  					name: "TestPlugin 1",
  2471  					inj:  injectedResult{PreBindStatus: int(framework.Success)},
  2472  				},
  2473  			},
  2474  			wantStatus: nil,
  2475  		},
  2476  		{
  2477  			name: "ErrorAndErrorPlugins",
  2478  			plugins: []*TestPlugin{
  2479  				{
  2480  					name: "TestPlugin",
  2481  					inj:  injectedResult{PreBindStatus: int(framework.Error)},
  2482  				},
  2483  				{
  2484  					name: "TestPlugin 1",
  2485  					inj:  injectedResult{PreBindStatus: int(framework.Error)},
  2486  				},
  2487  			},
  2488  			wantStatus: framework.AsStatus(fmt.Errorf(`running PreBind plugin "TestPlugin": %w`, errInjectedStatus)),
  2489  		},
  2490  		{
  2491  			name: "UnschedulableAndSuccessPreBindPlugin",
  2492  			plugins: []*TestPlugin{
  2493  				{
  2494  					name: "TestPlugin",
  2495  					inj:  injectedResult{PreBindStatus: int(framework.Unschedulable)},
  2496  				},
  2497  				{
  2498  					name: "TestPlugin 1",
  2499  					inj:  injectedResult{PreBindStatus: int(framework.Success)},
  2500  				},
  2501  			},
  2502  			wantStatus: framework.NewStatus(framework.Unschedulable, injectReason).WithPlugin("TestPlugin"),
  2503  		},
  2504  	}
  2505  
  2506  	for _, tt := range tests {
  2507  		t.Run(tt.name, func(t *testing.T) {
  2508  			registry := Registry{}
  2509  			configPlugins := &config.Plugins{}
  2510  
  2511  			for _, pl := range tt.plugins {
  2512  				tmpPl := pl
  2513  				if err := registry.Register(pl.name, func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
  2514  					return tmpPl, nil
  2515  				}); err != nil {
  2516  					t.Fatalf("Unable to register pre bind plugins: %s", pl.name)
  2517  				}
  2518  
  2519  				configPlugins.PreBind.Enabled = append(
  2520  					configPlugins.PreBind.Enabled,
  2521  					config.Plugin{Name: pl.name},
  2522  				)
  2523  			}
  2524  			profile := config.KubeSchedulerProfile{Plugins: configPlugins}
  2525  			ctx, cancel := context.WithCancel(context.Background())
  2526  			defer cancel()
  2527  			f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
  2528  			if err != nil {
  2529  				t.Fatalf("fail to create framework: %s", err)
  2530  			}
  2531  
  2532  			status := f.RunPreBindPlugins(ctx, nil, pod, "")
  2533  
  2534  			if !reflect.DeepEqual(status, tt.wantStatus) {
  2535  				t.Errorf("wrong status code. got %v, want %v", status, tt.wantStatus)
  2536  			}
  2537  		})
  2538  	}
  2539  }
  2540  
  2541  func TestReservePlugins(t *testing.T) {
  2542  	tests := []struct {
  2543  		name       string
  2544  		plugins    []*TestPlugin
  2545  		wantStatus *framework.Status
  2546  	}{
  2547  		{
  2548  			name:       "NoReservePlugin",
  2549  			plugins:    []*TestPlugin{},
  2550  			wantStatus: nil,
  2551  		},
  2552  		{
  2553  			name: "SuccessReservePlugins",
  2554  			plugins: []*TestPlugin{
  2555  				{
  2556  					name: "TestPlugin",
  2557  					inj:  injectedResult{ReserveStatus: int(framework.Success)},
  2558  				},
  2559  			},
  2560  			wantStatus: nil,
  2561  		},
  2562  		{
  2563  			name: "UnschedulableReservePlugin",
  2564  			plugins: []*TestPlugin{
  2565  				{
  2566  					name: "TestPlugin",
  2567  					inj:  injectedResult{ReserveStatus: int(framework.Unschedulable)},
  2568  				},
  2569  			},
  2570  			wantStatus: framework.NewStatus(framework.Unschedulable, injectReason).WithPlugin("TestPlugin"),
  2571  		},
  2572  		{
  2573  			name: "ErrorReservePlugin",
  2574  			plugins: []*TestPlugin{
  2575  				{
  2576  					name: "TestPlugin",
  2577  					inj:  injectedResult{ReserveStatus: int(framework.Error)},
  2578  				},
  2579  			},
  2580  			wantStatus: framework.AsStatus(fmt.Errorf(`running Reserve plugin "TestPlugin": %w`, errInjectedStatus)),
  2581  		},
  2582  		{
  2583  			name: "UnschedulableReservePlugin",
  2584  			plugins: []*TestPlugin{
  2585  				{
  2586  					name: "TestPlugin",
  2587  					inj:  injectedResult{ReserveStatus: int(framework.UnschedulableAndUnresolvable)},
  2588  				},
  2589  			},
  2590  			wantStatus: framework.NewStatus(framework.UnschedulableAndUnresolvable, injectReason).WithPlugin("TestPlugin"),
  2591  		},
  2592  		{
  2593  			name: "SuccessSuccessReservePlugins",
  2594  			plugins: []*TestPlugin{
  2595  				{
  2596  					name: "TestPlugin",
  2597  					inj:  injectedResult{ReserveStatus: int(framework.Success)},
  2598  				},
  2599  				{
  2600  					name: "TestPlugin 1",
  2601  					inj:  injectedResult{ReserveStatus: int(framework.Success)},
  2602  				},
  2603  			},
  2604  			wantStatus: nil,
  2605  		},
  2606  		{
  2607  			name: "ErrorErrorReservePlugins",
  2608  			plugins: []*TestPlugin{
  2609  				{
  2610  					name: "TestPlugin",
  2611  					inj:  injectedResult{ReserveStatus: int(framework.Error)},
  2612  				},
  2613  				{
  2614  					name: "TestPlugin 1",
  2615  					inj:  injectedResult{ReserveStatus: int(framework.Error)},
  2616  				},
  2617  			},
  2618  			wantStatus: framework.AsStatus(fmt.Errorf(`running Reserve plugin "TestPlugin": %w`, errInjectedStatus)),
  2619  		},
  2620  		{
  2621  			name: "SuccessErrorReservePlugins",
  2622  			plugins: []*TestPlugin{
  2623  				{
  2624  					name: "TestPlugin",
  2625  					inj:  injectedResult{ReserveStatus: int(framework.Success)},
  2626  				},
  2627  				{
  2628  					name: "TestPlugin 1",
  2629  					inj:  injectedResult{ReserveStatus: int(framework.Error)},
  2630  				},
  2631  			},
  2632  			wantStatus: framework.AsStatus(fmt.Errorf(`running Reserve plugin "TestPlugin 1": %w`, errInjectedStatus)),
  2633  		},
  2634  		{
  2635  			name: "ErrorSuccessReservePlugin",
  2636  			plugins: []*TestPlugin{
  2637  				{
  2638  					name: "TestPlugin",
  2639  					inj:  injectedResult{ReserveStatus: int(framework.Error)},
  2640  				},
  2641  				{
  2642  					name: "TestPlugin 1",
  2643  					inj:  injectedResult{ReserveStatus: int(framework.Success)},
  2644  				},
  2645  			},
  2646  			wantStatus: framework.AsStatus(fmt.Errorf(`running Reserve plugin "TestPlugin": %w`, errInjectedStatus)),
  2647  		},
  2648  		{
  2649  			name: "UnschedulableAndSuccessReservePlugin",
  2650  			plugins: []*TestPlugin{
  2651  				{
  2652  					name: "TestPlugin",
  2653  					inj:  injectedResult{ReserveStatus: int(framework.Unschedulable)},
  2654  				},
  2655  				{
  2656  					name: "TestPlugin 1",
  2657  					inj:  injectedResult{ReserveStatus: int(framework.Success)},
  2658  				},
  2659  			},
  2660  			wantStatus: framework.NewStatus(framework.Unschedulable, injectReason).WithPlugin("TestPlugin"),
  2661  		},
  2662  	}
  2663  
  2664  	for _, tt := range tests {
  2665  		t.Run(tt.name, func(t *testing.T) {
  2666  			registry := Registry{}
  2667  			configPlugins := &config.Plugins{}
  2668  
  2669  			for _, pl := range tt.plugins {
  2670  				tmpPl := pl
  2671  				if err := registry.Register(pl.name, func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
  2672  					return tmpPl, nil
  2673  				}); err != nil {
  2674  					t.Fatalf("Unable to register pre bind plugins: %s", pl.name)
  2675  				}
  2676  
  2677  				configPlugins.Reserve.Enabled = append(
  2678  					configPlugins.Reserve.Enabled,
  2679  					config.Plugin{Name: pl.name},
  2680  				)
  2681  			}
  2682  			profile := config.KubeSchedulerProfile{Plugins: configPlugins}
  2683  			ctx, cancel := context.WithCancel(context.Background())
  2684  			defer cancel()
  2685  			f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
  2686  			if err != nil {
  2687  				t.Fatalf("fail to create framework: %s", err)
  2688  			}
  2689  
  2690  			status := f.RunReservePluginsReserve(ctx, nil, pod, "")
  2691  
  2692  			if !reflect.DeepEqual(status, tt.wantStatus) {
  2693  				t.Errorf("wrong status code. got %v, want %v", status, tt.wantStatus)
  2694  			}
  2695  		})
  2696  	}
  2697  }
  2698  
  2699  func TestPermitPlugins(t *testing.T) {
  2700  	tests := []struct {
  2701  		name    string
  2702  		plugins []*TestPlugin
  2703  		want    *framework.Status
  2704  	}{
  2705  		{
  2706  			name:    "NilPermitPlugin",
  2707  			plugins: []*TestPlugin{},
  2708  			want:    nil,
  2709  		},
  2710  		{
  2711  			name: "SuccessPermitPlugin",
  2712  			plugins: []*TestPlugin{
  2713  				{
  2714  					name: "TestPlugin",
  2715  					inj:  injectedResult{PermitStatus: int(framework.Success)},
  2716  				},
  2717  			},
  2718  			want: nil,
  2719  		},
  2720  		{
  2721  			name: "UnschedulablePermitPlugin",
  2722  			plugins: []*TestPlugin{
  2723  				{
  2724  					name: "TestPlugin",
  2725  					inj:  injectedResult{PermitStatus: int(framework.Unschedulable)},
  2726  				},
  2727  			},
  2728  			want: framework.NewStatus(framework.Unschedulable, injectReason).WithPlugin("TestPlugin"),
  2729  		},
  2730  		{
  2731  			name: "ErrorPermitPlugin",
  2732  			plugins: []*TestPlugin{
  2733  				{
  2734  					name: "TestPlugin",
  2735  					inj:  injectedResult{PermitStatus: int(framework.Error)},
  2736  				},
  2737  			},
  2738  			want: framework.AsStatus(fmt.Errorf(`running Permit plugin "TestPlugin": %w`, errInjectedStatus)).WithPlugin("TestPlugin"),
  2739  		},
  2740  		{
  2741  			name: "UnschedulableAndUnresolvablePermitPlugin",
  2742  			plugins: []*TestPlugin{
  2743  				{
  2744  					name: "TestPlugin",
  2745  					inj:  injectedResult{PermitStatus: int(framework.UnschedulableAndUnresolvable)},
  2746  				},
  2747  			},
  2748  			want: framework.NewStatus(framework.UnschedulableAndUnresolvable, injectReason).WithPlugin("TestPlugin"),
  2749  		},
  2750  		{
  2751  			name: "WaitPermitPlugin",
  2752  			plugins: []*TestPlugin{
  2753  				{
  2754  					name: "TestPlugin",
  2755  					inj:  injectedResult{PermitStatus: int(framework.Wait)},
  2756  				},
  2757  			},
  2758  			want: framework.NewStatus(framework.Wait, `one or more plugins asked to wait and no plugin rejected pod ""`),
  2759  		},
  2760  		{
  2761  			name: "SuccessSuccessPermitPlugin",
  2762  			plugins: []*TestPlugin{
  2763  				{
  2764  					name: "TestPlugin",
  2765  					inj:  injectedResult{PermitStatus: int(framework.Success)},
  2766  				},
  2767  				{
  2768  					name: "TestPlugin 1",
  2769  					inj:  injectedResult{PermitStatus: int(framework.Success)},
  2770  				},
  2771  			},
  2772  			want: nil,
  2773  		},
  2774  		{
  2775  			name: "ErrorAndErrorPlugins",
  2776  			plugins: []*TestPlugin{
  2777  				{
  2778  					name: "TestPlugin",
  2779  					inj:  injectedResult{PermitStatus: int(framework.Error)},
  2780  				},
  2781  				{
  2782  					name: "TestPlugin 1",
  2783  					inj:  injectedResult{PermitStatus: int(framework.Error)},
  2784  				},
  2785  			},
  2786  			want: framework.AsStatus(fmt.Errorf(`running Permit plugin "TestPlugin": %w`, errInjectedStatus)).WithPlugin("TestPlugin"),
  2787  		},
  2788  	}
  2789  
  2790  	for _, tt := range tests {
  2791  		t.Run(tt.name, func(t *testing.T) {
  2792  			registry := Registry{}
  2793  			configPlugins := &config.Plugins{}
  2794  
  2795  			for _, pl := range tt.plugins {
  2796  				tmpPl := pl
  2797  				if err := registry.Register(pl.name, func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
  2798  					return tmpPl, nil
  2799  				}); err != nil {
  2800  					t.Fatalf("Unable to register Permit plugin: %s", pl.name)
  2801  				}
  2802  
  2803  				configPlugins.Permit.Enabled = append(
  2804  					configPlugins.Permit.Enabled,
  2805  					config.Plugin{Name: pl.name},
  2806  				)
  2807  			}
  2808  			profile := config.KubeSchedulerProfile{Plugins: configPlugins}
  2809  			ctx, cancel := context.WithCancel(context.Background())
  2810  			defer cancel()
  2811  			f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
  2812  			if err != nil {
  2813  				t.Fatalf("fail to create framework: %s", err)
  2814  			}
  2815  
  2816  			status := f.RunPermitPlugins(ctx, nil, pod, "")
  2817  			if !reflect.DeepEqual(status, tt.want) {
  2818  				t.Errorf("wrong status code. got %v, want %v", status, tt.want)
  2819  			}
  2820  		})
  2821  	}
  2822  }
  2823  
  2824  // withMetricsRecorder set metricsRecorder for the scheduling frameworkImpl.
  2825  func withMetricsRecorder(recorder *metrics.MetricAsyncRecorder) Option {
  2826  	return func(o *frameworkOptions) {
  2827  		o.metricsRecorder = recorder
  2828  	}
  2829  }
  2830  
  2831  func TestRecordingMetrics(t *testing.T) {
  2832  	state := &framework.CycleState{}
  2833  	state.SetRecordPluginMetrics(true)
  2834  
  2835  	tests := []struct {
  2836  		name               string
  2837  		action             func(f framework.Framework)
  2838  		inject             injectedResult
  2839  		wantExtensionPoint string
  2840  		wantStatus         framework.Code
  2841  	}{
  2842  		{
  2843  			name:               "PreFilter - Success",
  2844  			action:             func(f framework.Framework) { f.RunPreFilterPlugins(context.Background(), state, pod) },
  2845  			wantExtensionPoint: "PreFilter",
  2846  			wantStatus:         framework.Success,
  2847  		},
  2848  		{
  2849  			name:               "PreScore - Success",
  2850  			action:             func(f framework.Framework) { f.RunPreScorePlugins(context.Background(), state, pod, nil) },
  2851  			wantExtensionPoint: "PreScore",
  2852  			wantStatus:         framework.Success,
  2853  		},
  2854  		{
  2855  			name: "Score - Success",
  2856  			action: func(f framework.Framework) {
  2857  				f.RunScorePlugins(context.Background(), state, pod, BuildNodeInfos(nodes))
  2858  			},
  2859  			wantExtensionPoint: "Score",
  2860  			wantStatus:         framework.Success,
  2861  		},
  2862  		{
  2863  			name:               "Reserve - Success",
  2864  			action:             func(f framework.Framework) { f.RunReservePluginsReserve(context.Background(), state, pod, "") },
  2865  			wantExtensionPoint: "Reserve",
  2866  			wantStatus:         framework.Success,
  2867  		},
  2868  		{
  2869  			name:               "Unreserve - Success",
  2870  			action:             func(f framework.Framework) { f.RunReservePluginsUnreserve(context.Background(), state, pod, "") },
  2871  			wantExtensionPoint: "Unreserve",
  2872  			wantStatus:         framework.Success,
  2873  		},
  2874  		{
  2875  			name:               "PreBind - Success",
  2876  			action:             func(f framework.Framework) { f.RunPreBindPlugins(context.Background(), state, pod, "") },
  2877  			wantExtensionPoint: "PreBind",
  2878  			wantStatus:         framework.Success,
  2879  		},
  2880  		{
  2881  			name:               "Bind - Success",
  2882  			action:             func(f framework.Framework) { f.RunBindPlugins(context.Background(), state, pod, "") },
  2883  			wantExtensionPoint: "Bind",
  2884  			wantStatus:         framework.Success,
  2885  		},
  2886  		{
  2887  			name:               "PostBind - Success",
  2888  			action:             func(f framework.Framework) { f.RunPostBindPlugins(context.Background(), state, pod, "") },
  2889  			wantExtensionPoint: "PostBind",
  2890  			wantStatus:         framework.Success,
  2891  		},
  2892  		{
  2893  			name:               "Permit - Success",
  2894  			action:             func(f framework.Framework) { f.RunPermitPlugins(context.Background(), state, pod, "") },
  2895  			wantExtensionPoint: "Permit",
  2896  			wantStatus:         framework.Success,
  2897  		},
  2898  
  2899  		{
  2900  			name:               "PreFilter - Error",
  2901  			action:             func(f framework.Framework) { f.RunPreFilterPlugins(context.Background(), state, pod) },
  2902  			inject:             injectedResult{PreFilterStatus: int(framework.Error)},
  2903  			wantExtensionPoint: "PreFilter",
  2904  			wantStatus:         framework.Error,
  2905  		},
  2906  		{
  2907  			name:               "PreScore - Error",
  2908  			action:             func(f framework.Framework) { f.RunPreScorePlugins(context.Background(), state, pod, nil) },
  2909  			inject:             injectedResult{PreScoreStatus: int(framework.Error)},
  2910  			wantExtensionPoint: "PreScore",
  2911  			wantStatus:         framework.Error,
  2912  		},
  2913  		{
  2914  			name: "Score - Error",
  2915  			action: func(f framework.Framework) {
  2916  				f.RunScorePlugins(context.Background(), state, pod, BuildNodeInfos(nodes))
  2917  			},
  2918  			inject:             injectedResult{ScoreStatus: int(framework.Error)},
  2919  			wantExtensionPoint: "Score",
  2920  			wantStatus:         framework.Error,
  2921  		},
  2922  		{
  2923  			name:               "Reserve - Error",
  2924  			action:             func(f framework.Framework) { f.RunReservePluginsReserve(context.Background(), state, pod, "") },
  2925  			inject:             injectedResult{ReserveStatus: int(framework.Error)},
  2926  			wantExtensionPoint: "Reserve",
  2927  			wantStatus:         framework.Error,
  2928  		},
  2929  		{
  2930  			name:               "PreBind - Error",
  2931  			action:             func(f framework.Framework) { f.RunPreBindPlugins(context.Background(), state, pod, "") },
  2932  			inject:             injectedResult{PreBindStatus: int(framework.Error)},
  2933  			wantExtensionPoint: "PreBind",
  2934  			wantStatus:         framework.Error,
  2935  		},
  2936  		{
  2937  			name:               "Bind - Error",
  2938  			action:             func(f framework.Framework) { f.RunBindPlugins(context.Background(), state, pod, "") },
  2939  			inject:             injectedResult{BindStatus: int(framework.Error)},
  2940  			wantExtensionPoint: "Bind",
  2941  			wantStatus:         framework.Error,
  2942  		},
  2943  		{
  2944  			name:               "Permit - Error",
  2945  			action:             func(f framework.Framework) { f.RunPermitPlugins(context.Background(), state, pod, "") },
  2946  			inject:             injectedResult{PermitStatus: int(framework.Error)},
  2947  			wantExtensionPoint: "Permit",
  2948  			wantStatus:         framework.Error,
  2949  		},
  2950  		{
  2951  			name:               "Permit - Wait",
  2952  			action:             func(f framework.Framework) { f.RunPermitPlugins(context.Background(), state, pod, "") },
  2953  			inject:             injectedResult{PermitStatus: int(framework.Wait)},
  2954  			wantExtensionPoint: "Permit",
  2955  			wantStatus:         framework.Wait,
  2956  		},
  2957  	}
  2958  
  2959  	for _, tt := range tests {
  2960  		t.Run(tt.name, func(t *testing.T) {
  2961  			metrics.Register()
  2962  			metrics.FrameworkExtensionPointDuration.Reset()
  2963  			metrics.PluginExecutionDuration.Reset()
  2964  
  2965  			plugin := &TestPlugin{name: testPlugin, inj: tt.inject}
  2966  			r := make(Registry)
  2967  			r.Register(testPlugin,
  2968  				func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
  2969  					return plugin, nil
  2970  				})
  2971  			pluginSet := config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin, Weight: 1}}}
  2972  			plugins := &config.Plugins{
  2973  				Score:     pluginSet,
  2974  				PreFilter: pluginSet,
  2975  				Filter:    pluginSet,
  2976  				PreScore:  pluginSet,
  2977  				Reserve:   pluginSet,
  2978  				Permit:    pluginSet,
  2979  				PreBind:   pluginSet,
  2980  				Bind:      pluginSet,
  2981  				PostBind:  pluginSet,
  2982  			}
  2983  
  2984  			_, ctx := ktesting.NewTestContext(t)
  2985  			ctx, cancel := context.WithCancel(ctx)
  2986  
  2987  			recorder := metrics.NewMetricsAsyncRecorder(100, time.Nanosecond, ctx.Done())
  2988  			profile := config.KubeSchedulerProfile{
  2989  				PercentageOfNodesToScore: ptr.To[int32](testPercentageOfNodesToScore),
  2990  				SchedulerName:            testProfileName,
  2991  				Plugins:                  plugins,
  2992  			}
  2993  			f, err := newFrameworkWithQueueSortAndBind(ctx, r, profile, withMetricsRecorder(recorder))
  2994  			if err != nil {
  2995  				cancel()
  2996  				t.Fatalf("Failed to create framework for testing: %v", err)
  2997  			}
  2998  
  2999  			tt.action(f)
  3000  
  3001  			// Stop the goroutine which records metrics and ensure it's stopped.
  3002  			cancel()
  3003  			<-recorder.IsStoppedCh
  3004  			// Try to clean up the metrics buffer again in case it's not empty.
  3005  			recorder.FlushMetrics()
  3006  
  3007  			collectAndCompareFrameworkMetrics(t, tt.wantExtensionPoint, tt.wantStatus)
  3008  			collectAndComparePluginMetrics(t, tt.wantExtensionPoint, testPlugin, tt.wantStatus)
  3009  		})
  3010  	}
  3011  }
  3012  
  3013  func TestRunBindPlugins(t *testing.T) {
  3014  	tests := []struct {
  3015  		name       string
  3016  		injects    []framework.Code
  3017  		wantStatus framework.Code
  3018  	}{
  3019  		{
  3020  			name:       "simple success",
  3021  			injects:    []framework.Code{framework.Success},
  3022  			wantStatus: framework.Success,
  3023  		},
  3024  		{
  3025  			name:       "error on second",
  3026  			injects:    []framework.Code{framework.Skip, framework.Error, framework.Success},
  3027  			wantStatus: framework.Error,
  3028  		},
  3029  		{
  3030  			name:       "all skip",
  3031  			injects:    []framework.Code{framework.Skip, framework.Skip, framework.Skip},
  3032  			wantStatus: framework.Skip,
  3033  		},
  3034  		{
  3035  			name:       "error on third, but not reached",
  3036  			injects:    []framework.Code{framework.Skip, framework.Success, framework.Error},
  3037  			wantStatus: framework.Success,
  3038  		},
  3039  		{
  3040  			name:       "no bind plugin, returns default binder",
  3041  			injects:    []framework.Code{},
  3042  			wantStatus: framework.Success,
  3043  		},
  3044  		{
  3045  			name:       "invalid status",
  3046  			injects:    []framework.Code{framework.Unschedulable},
  3047  			wantStatus: framework.Unschedulable,
  3048  		},
  3049  		{
  3050  			name:       "simple error",
  3051  			injects:    []framework.Code{framework.Error},
  3052  			wantStatus: framework.Error,
  3053  		},
  3054  		{
  3055  			name:       "success on second, returns success",
  3056  			injects:    []framework.Code{framework.Skip, framework.Success},
  3057  			wantStatus: framework.Success,
  3058  		},
  3059  		{
  3060  			name:       "invalid status, returns error",
  3061  			injects:    []framework.Code{framework.Skip, framework.UnschedulableAndUnresolvable},
  3062  			wantStatus: framework.UnschedulableAndUnresolvable,
  3063  		},
  3064  		{
  3065  			name:       "error after success status, returns success",
  3066  			injects:    []framework.Code{framework.Success, framework.Error},
  3067  			wantStatus: framework.Success,
  3068  		},
  3069  		{
  3070  			name:       "success before invalid status, returns success",
  3071  			injects:    []framework.Code{framework.Success, framework.Error},
  3072  			wantStatus: framework.Success,
  3073  		},
  3074  		{
  3075  			name:       "success after error status, returns error",
  3076  			injects:    []framework.Code{framework.Error, framework.Success},
  3077  			wantStatus: framework.Error,
  3078  		},
  3079  	}
  3080  	for _, tt := range tests {
  3081  		t.Run(tt.name, func(t *testing.T) {
  3082  			metrics.Register()
  3083  			metrics.FrameworkExtensionPointDuration.Reset()
  3084  			metrics.PluginExecutionDuration.Reset()
  3085  
  3086  			pluginSet := config.PluginSet{}
  3087  			r := make(Registry)
  3088  			for i, inj := range tt.injects {
  3089  				name := fmt.Sprintf("bind-%d", i)
  3090  				plugin := &TestPlugin{name: name, inj: injectedResult{BindStatus: int(inj)}}
  3091  				r.Register(name,
  3092  					func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
  3093  						return plugin, nil
  3094  					})
  3095  				pluginSet.Enabled = append(pluginSet.Enabled, config.Plugin{Name: name})
  3096  			}
  3097  			plugins := &config.Plugins{Bind: pluginSet}
  3098  			_, ctx := ktesting.NewTestContext(t)
  3099  			ctx, cancel := context.WithCancel(ctx)
  3100  			recorder := metrics.NewMetricsAsyncRecorder(100, time.Nanosecond, ctx.Done())
  3101  			profile := config.KubeSchedulerProfile{
  3102  				SchedulerName:            testProfileName,
  3103  				PercentageOfNodesToScore: ptr.To[int32](testPercentageOfNodesToScore),
  3104  				Plugins:                  plugins,
  3105  			}
  3106  			fwk, err := newFrameworkWithQueueSortAndBind(ctx, r, profile, withMetricsRecorder(recorder))
  3107  			if err != nil {
  3108  				cancel()
  3109  				t.Fatal(err)
  3110  			}
  3111  
  3112  			st := fwk.RunBindPlugins(context.Background(), state, pod, "")
  3113  			if st.Code() != tt.wantStatus {
  3114  				t.Errorf("got status code %s, want %s", st.Code(), tt.wantStatus)
  3115  			}
  3116  
  3117  			// Stop the goroutine which records metrics and ensure it's stopped.
  3118  			cancel()
  3119  			<-recorder.IsStoppedCh
  3120  			// Try to clean up the metrics buffer again in case it's not empty.
  3121  			recorder.FlushMetrics()
  3122  			collectAndCompareFrameworkMetrics(t, "Bind", tt.wantStatus)
  3123  		})
  3124  	}
  3125  }
  3126  
  3127  func TestPermitWaitDurationMetric(t *testing.T) {
  3128  	tests := []struct {
  3129  		name    string
  3130  		inject  injectedResult
  3131  		wantRes string
  3132  	}{
  3133  		{
  3134  			name: "WaitOnPermit - No Wait",
  3135  		},
  3136  		{
  3137  			name:    "WaitOnPermit - Wait Timeout",
  3138  			inject:  injectedResult{PermitStatus: int(framework.Wait)},
  3139  			wantRes: "Unschedulable",
  3140  		},
  3141  	}
  3142  
  3143  	for _, tt := range tests {
  3144  		t.Run(tt.name, func(t *testing.T) {
  3145  			metrics.Register()
  3146  			metrics.PermitWaitDuration.Reset()
  3147  
  3148  			plugin := &TestPlugin{name: testPlugin, inj: tt.inject}
  3149  			r := make(Registry)
  3150  			err := r.Register(testPlugin,
  3151  				func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
  3152  					return plugin, nil
  3153  				})
  3154  			if err != nil {
  3155  				t.Fatal(err)
  3156  			}
  3157  			plugins := &config.Plugins{
  3158  				Permit: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin, Weight: 1}}},
  3159  			}
  3160  			profile := config.KubeSchedulerProfile{Plugins: plugins}
  3161  			ctx, cancel := context.WithCancel(context.Background())
  3162  			defer cancel()
  3163  			f, err := newFrameworkWithQueueSortAndBind(ctx, r, profile)
  3164  			if err != nil {
  3165  				t.Fatalf("Failed to create framework for testing: %v", err)
  3166  			}
  3167  
  3168  			f.RunPermitPlugins(ctx, nil, pod, "")
  3169  			f.WaitOnPermit(ctx, pod)
  3170  
  3171  			collectAndComparePermitWaitDuration(t, tt.wantRes)
  3172  		})
  3173  	}
  3174  }
  3175  
  3176  func TestWaitOnPermit(t *testing.T) {
  3177  	pod := &v1.Pod{
  3178  		ObjectMeta: metav1.ObjectMeta{
  3179  			Name: "pod",
  3180  			UID:  types.UID("pod"),
  3181  		},
  3182  	}
  3183  
  3184  	tests := []struct {
  3185  		name   string
  3186  		action func(f framework.Framework)
  3187  		want   *framework.Status
  3188  	}{
  3189  		{
  3190  			name: "Reject Waiting Pod",
  3191  			action: func(f framework.Framework) {
  3192  				f.GetWaitingPod(pod.UID).Reject(permitPlugin, "reject message")
  3193  			},
  3194  			want: framework.NewStatus(framework.Unschedulable, "reject message").WithPlugin(permitPlugin),
  3195  		},
  3196  		{
  3197  			name: "Allow Waiting Pod",
  3198  			action: func(f framework.Framework) {
  3199  				f.GetWaitingPod(pod.UID).Allow(permitPlugin)
  3200  			},
  3201  			want: nil,
  3202  		},
  3203  	}
  3204  
  3205  	for _, tt := range tests {
  3206  		t.Run(tt.name, func(t *testing.T) {
  3207  			testPermitPlugin := &TestPermitPlugin{}
  3208  			r := make(Registry)
  3209  			r.Register(permitPlugin,
  3210  				func(_ context.Context, _ runtime.Object, fh framework.Handle) (framework.Plugin, error) {
  3211  					return testPermitPlugin, nil
  3212  				})
  3213  			plugins := &config.Plugins{
  3214  				Permit: config.PluginSet{Enabled: []config.Plugin{{Name: permitPlugin, Weight: 1}}},
  3215  			}
  3216  			profile := config.KubeSchedulerProfile{Plugins: plugins}
  3217  			ctx, cancel := context.WithCancel(context.Background())
  3218  			defer cancel()
  3219  			f, err := newFrameworkWithQueueSortAndBind(ctx, r, profile)
  3220  			if err != nil {
  3221  				t.Fatalf("Failed to create framework for testing: %v", err)
  3222  			}
  3223  
  3224  			runPermitPluginsStatus := f.RunPermitPlugins(ctx, nil, pod, "")
  3225  			if runPermitPluginsStatus.Code() != framework.Wait {
  3226  				t.Fatalf("Expected RunPermitPlugins to return status %v, but got %v",
  3227  					framework.Wait, runPermitPluginsStatus.Code())
  3228  			}
  3229  
  3230  			go tt.action(f)
  3231  
  3232  			got := f.WaitOnPermit(ctx, pod)
  3233  			if !reflect.DeepEqual(tt.want, got) {
  3234  				t.Errorf("Unexpected status: want %v, but got %v", tt.want, got)
  3235  			}
  3236  		})
  3237  	}
  3238  }
  3239  
  3240  func TestListPlugins(t *testing.T) {
  3241  	tests := []struct {
  3242  		name    string
  3243  		plugins *config.Plugins
  3244  		want    *config.Plugins
  3245  	}{
  3246  		{
  3247  			name:    "Add empty plugin",
  3248  			plugins: &config.Plugins{},
  3249  			want: &config.Plugins{
  3250  				QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: queueSortPlugin}}},
  3251  				Bind:      config.PluginSet{Enabled: []config.Plugin{{Name: bindPlugin}}},
  3252  			},
  3253  		},
  3254  		{
  3255  			name: "Add multiple plugins",
  3256  			plugins: &config.Plugins{
  3257  				Score: config.PluginSet{Enabled: []config.Plugin{{Name: scorePlugin1, Weight: 3}, {Name: scoreWithNormalizePlugin1}}},
  3258  			},
  3259  			want: &config.Plugins{
  3260  				QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: queueSortPlugin}}},
  3261  				Bind:      config.PluginSet{Enabled: []config.Plugin{{Name: bindPlugin}}},
  3262  				Score:     config.PluginSet{Enabled: []config.Plugin{{Name: scorePlugin1, Weight: 3}, {Name: scoreWithNormalizePlugin1, Weight: 1}}},
  3263  			},
  3264  		},
  3265  	}
  3266  
  3267  	for _, tt := range tests {
  3268  		t.Run(tt.name, func(t *testing.T) {
  3269  			profile := config.KubeSchedulerProfile{Plugins: tt.plugins}
  3270  			_, ctx := ktesting.NewTestContext(t)
  3271  			ctx, cancel := context.WithCancel(ctx)
  3272  			defer cancel()
  3273  			f, err := newFrameworkWithQueueSortAndBind(ctx, registry, profile)
  3274  			if err != nil {
  3275  				t.Fatalf("Failed to create framework for testing: %v", err)
  3276  			}
  3277  			got := f.ListPlugins()
  3278  			if diff := cmp.Diff(tt.want, got); diff != "" {
  3279  				t.Errorf("unexpected plugins (-want,+got):\n%s", diff)
  3280  			}
  3281  		})
  3282  	}
  3283  }
  3284  
  3285  func TestClose(t *testing.T) {
  3286  	tests := []struct {
  3287  		name    string
  3288  		plugins *config.Plugins
  3289  		wantErr error
  3290  	}{
  3291  		{
  3292  			name: "close doesn't return error",
  3293  			plugins: &config.Plugins{
  3294  				MultiPoint: config.PluginSet{
  3295  					Enabled: []config.Plugin{
  3296  						{Name: testPlugin, Weight: 5},
  3297  					},
  3298  				},
  3299  			},
  3300  		},
  3301  		{
  3302  			name: "close returns error",
  3303  			plugins: &config.Plugins{
  3304  				MultiPoint: config.PluginSet{
  3305  					Enabled: []config.Plugin{
  3306  						{Name: testPlugin, Weight: 5},
  3307  						{Name: testCloseErrorPlugin},
  3308  					},
  3309  				},
  3310  			},
  3311  			wantErr: errClose,
  3312  		},
  3313  	}
  3314  
  3315  	for _, tc := range tests {
  3316  		t.Run(tc.name, func(t *testing.T) {
  3317  			_, ctx := ktesting.NewTestContext(t)
  3318  			ctx, cancel := context.WithCancel(ctx)
  3319  			defer cancel()
  3320  			fw, err := NewFramework(ctx, registry, &config.KubeSchedulerProfile{Plugins: tc.plugins})
  3321  			if err != nil {
  3322  				t.Fatalf("Unexpected error during calling NewFramework, got %v", err)
  3323  			}
  3324  			err = fw.Close()
  3325  			if !errors.Is(err, tc.wantErr) {
  3326  				t.Fatalf("Unexpected error from Close(), got: %v, want: %v", err, tc.wantErr)
  3327  			}
  3328  		})
  3329  	}
  3330  }
  3331  
  3332  func buildScoreConfigDefaultWeights(ps ...string) *config.Plugins {
  3333  	return buildScoreConfigWithWeights(defaultWeights, ps...)
  3334  }
  3335  
  3336  func buildScoreConfigWithWeights(weights map[string]int32, ps ...string) *config.Plugins {
  3337  	var plugins []config.Plugin
  3338  	for _, p := range ps {
  3339  		plugins = append(plugins, config.Plugin{Name: p, Weight: weights[p]})
  3340  	}
  3341  	return &config.Plugins{Score: config.PluginSet{Enabled: plugins}}
  3342  }
  3343  
  3344  type injectedResult struct {
  3345  	ScoreRes                 int64                      `json:"scoreRes,omitempty"`
  3346  	NormalizeRes             int64                      `json:"normalizeRes,omitempty"`
  3347  	ScoreStatus              int                        `json:"scoreStatus,omitempty"`
  3348  	NormalizeStatus          int                        `json:"normalizeStatus,omitempty"`
  3349  	PreFilterResult          *framework.PreFilterResult `json:"preFilterResult,omitempty"`
  3350  	PreFilterStatus          int                        `json:"preFilterStatus,omitempty"`
  3351  	PreFilterAddPodStatus    int                        `json:"preFilterAddPodStatus,omitempty"`
  3352  	PreFilterRemovePodStatus int                        `json:"preFilterRemovePodStatus,omitempty"`
  3353  	FilterStatus             int                        `json:"filterStatus,omitempty"`
  3354  	PostFilterStatus         int                        `json:"postFilterStatus,omitempty"`
  3355  	PreScoreStatus           int                        `json:"preScoreStatus,omitempty"`
  3356  	ReserveStatus            int                        `json:"reserveStatus,omitempty"`
  3357  	PreBindStatus            int                        `json:"preBindStatus,omitempty"`
  3358  	BindStatus               int                        `json:"bindStatus,omitempty"`
  3359  	PermitStatus             int                        `json:"permitStatus,omitempty"`
  3360  }
  3361  
  3362  func setScoreRes(inj injectedResult) (int64, *framework.Status) {
  3363  	if framework.Code(inj.ScoreStatus) != framework.Success {
  3364  		return 0, framework.NewStatus(framework.Code(inj.ScoreStatus), "injecting failure.")
  3365  	}
  3366  	return inj.ScoreRes, nil
  3367  }
  3368  
  3369  func injectNormalizeRes(inj injectedResult, scores framework.NodeScoreList) *framework.Status {
  3370  	if framework.Code(inj.NormalizeStatus) != framework.Success {
  3371  		return framework.NewStatus(framework.Code(inj.NormalizeStatus), "injecting failure.")
  3372  	}
  3373  	for i := range scores {
  3374  		scores[i].Score = inj.NormalizeRes
  3375  	}
  3376  	return nil
  3377  }
  3378  
  3379  func collectAndComparePluginMetrics(t *testing.T, wantExtensionPoint, wantPlugin string, wantStatus framework.Code) {
  3380  	t.Helper()
  3381  	m := metrics.PluginExecutionDuration.WithLabelValues(wantPlugin, wantExtensionPoint, wantStatus.String())
  3382  
  3383  	count, err := testutil.GetHistogramMetricCount(m)
  3384  	if err != nil {
  3385  		t.Errorf("Failed to get %s sampleCount, err: %v", metrics.PluginExecutionDuration.Name, err)
  3386  	}
  3387  	if count == 0 {
  3388  		t.Error("Expect at least 1 sample")
  3389  	}
  3390  	value, err := testutil.GetHistogramMetricValue(m)
  3391  	if err != nil {
  3392  		t.Errorf("Failed to get %s value, err: %v", metrics.PluginExecutionDuration.Name, err)
  3393  	}
  3394  	checkLatency(t, value)
  3395  }
  3396  
  3397  func collectAndCompareFrameworkMetrics(t *testing.T, wantExtensionPoint string, wantStatus framework.Code) {
  3398  	t.Helper()
  3399  	m := metrics.FrameworkExtensionPointDuration.WithLabelValues(wantExtensionPoint, wantStatus.String(), testProfileName)
  3400  
  3401  	count, err := testutil.GetHistogramMetricCount(m)
  3402  	if err != nil {
  3403  		t.Errorf("Failed to get %s sampleCount, err: %v", metrics.FrameworkExtensionPointDuration.Name, err)
  3404  	}
  3405  	if count != 1 {
  3406  		t.Errorf("Expect 1 sample, got: %v", count)
  3407  	}
  3408  	value, err := testutil.GetHistogramMetricValue(m)
  3409  	if err != nil {
  3410  		t.Errorf("Failed to get %s value, err: %v", metrics.FrameworkExtensionPointDuration.Name, err)
  3411  	}
  3412  	checkLatency(t, value)
  3413  }
  3414  
  3415  func collectAndComparePermitWaitDuration(t *testing.T, wantRes string) {
  3416  	m := metrics.PermitWaitDuration.WithLabelValues(wantRes)
  3417  	count, err := testutil.GetHistogramMetricCount(m)
  3418  	if err != nil {
  3419  		t.Errorf("Failed to get %s sampleCount, err: %v", metrics.PermitWaitDuration.Name, err)
  3420  	}
  3421  	if wantRes == "" {
  3422  		if count != 0 {
  3423  			t.Errorf("Expect 0 sample, got: %v", count)
  3424  		}
  3425  	} else {
  3426  		if count != 1 {
  3427  			t.Errorf("Expect 1 sample, got: %v", count)
  3428  		}
  3429  		value, err := testutil.GetHistogramMetricValue(m)
  3430  		if err != nil {
  3431  			t.Errorf("Failed to get %s value, err: %v", metrics.PermitWaitDuration.Name, err)
  3432  		}
  3433  		checkLatency(t, value)
  3434  	}
  3435  }
  3436  
  3437  func mustNewPodInfo(t *testing.T, pod *v1.Pod) *framework.PodInfo {
  3438  	podInfo, err := framework.NewPodInfo(pod)
  3439  	if err != nil {
  3440  		t.Fatal(err)
  3441  	}
  3442  	return podInfo
  3443  }
  3444  
  3445  // BuildNodeInfos build NodeInfo slice from a v1.Node slice
  3446  func BuildNodeInfos(nodes []*v1.Node) []*framework.NodeInfo {
  3447  	res := make([]*framework.NodeInfo, len(nodes))
  3448  	for i := 0; i < len(nodes); i++ {
  3449  		res[i] = framework.NewNodeInfo()
  3450  		res[i].SetNode(nodes[i])
  3451  	}
  3452  	return res
  3453  }
  3454  

View as plain text