Added tests for metrics and framework package of pkg/scheduler
Signed-off-by: Anuj Agrawal <anujagrawal380@gmail.com>
This commit is contained in:
parent
6b18b6e120
commit
39d3ef3b11
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
Copyright 2024 The Karmada Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPluginToResult_Merge(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
results PluginToResult
|
||||
want *Result
|
||||
}{
|
||||
{
|
||||
name: "empty results",
|
||||
results: PluginToResult{},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "all success results",
|
||||
results: PluginToResult{
|
||||
"plugin1": NewResult(Success),
|
||||
"plugin2": NewResult(Success),
|
||||
},
|
||||
want: NewResult(Success),
|
||||
},
|
||||
{
|
||||
name: "mixed results with unschedulable",
|
||||
results: PluginToResult{
|
||||
"plugin1": NewResult(Success),
|
||||
"plugin2": NewResult(Unschedulable, "reason1"),
|
||||
"plugin3": NewResult(Success),
|
||||
},
|
||||
want: NewResult(Unschedulable, "reason1"),
|
||||
},
|
||||
{
|
||||
name: "mixed results with error",
|
||||
results: PluginToResult{
|
||||
"plugin1": NewResult(Success),
|
||||
"plugin2": NewResult(Unschedulable, "reason1"),
|
||||
"plugin3": NewResult(Error, "error occurred"),
|
||||
},
|
||||
want: NewResult(Error, "reason1", "error occurred"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := tt.results.Merge()
|
||||
if tt.want == nil {
|
||||
assert.Nil(t, got)
|
||||
} else {
|
||||
assert.NotNil(t, got)
|
||||
assert.Equal(t, tt.want.code, got.code)
|
||||
|
||||
// Sort the reasons before comparing
|
||||
sortedWantReasons := make([]string, len(tt.want.reasons))
|
||||
copy(sortedWantReasons, tt.want.reasons)
|
||||
sort.Strings(sortedWantReasons)
|
||||
|
||||
sortedGotReasons := make([]string, len(got.reasons))
|
||||
copy(sortedGotReasons, got.reasons)
|
||||
sort.Strings(sortedGotReasons)
|
||||
|
||||
assert.Equal(t, sortedWantReasons, sortedGotReasons)
|
||||
|
||||
if tt.want.err != nil {
|
||||
assert.Error(t, got.err)
|
||||
} else {
|
||||
assert.NoError(t, got.err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResult_IsSuccess(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
result *Result
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "nil result",
|
||||
result: nil,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "success result",
|
||||
result: NewResult(Success),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "unschedulable result",
|
||||
result: NewResult(Unschedulable),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "error result",
|
||||
result: NewResult(Error),
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, tt.result.IsSuccess())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResult_AsError(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
result *Result
|
||||
wantErr bool
|
||||
errorMsg string
|
||||
}{
|
||||
{
|
||||
name: "success result",
|
||||
result: NewResult(Success),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "unschedulable result",
|
||||
result: NewResult(Unschedulable, "reason1", "reason2"),
|
||||
wantErr: true,
|
||||
errorMsg: "reason1, reason2",
|
||||
},
|
||||
{
|
||||
name: "error result",
|
||||
result: NewResult(Error, "error occurred"),
|
||||
wantErr: true,
|
||||
errorMsg: "error occurred",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.result.AsError()
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.errorMsg, err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResult_AsResult(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
wantCode Code
|
||||
wantReasons []string
|
||||
}{
|
||||
{
|
||||
name: "non-nil error",
|
||||
err: errors.New("test error"),
|
||||
wantCode: Error,
|
||||
wantReasons: []string{"test error"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := AsResult(tt.err)
|
||||
assert.Equal(t, tt.wantCode, got.code)
|
||||
assert.Equal(t, tt.wantReasons, got.reasons)
|
||||
assert.Equal(t, tt.err, got.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResult_Code(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
result *Result
|
||||
want Code
|
||||
}{
|
||||
{
|
||||
name: "nil result",
|
||||
result: nil,
|
||||
want: Success,
|
||||
},
|
||||
{
|
||||
name: "success result",
|
||||
result: NewResult(Success),
|
||||
want: Success,
|
||||
},
|
||||
{
|
||||
name: "unschedulable result",
|
||||
result: NewResult(Unschedulable),
|
||||
want: Unschedulable,
|
||||
},
|
||||
{
|
||||
name: "error result",
|
||||
result: NewResult(Error),
|
||||
want: Error,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := tt.result.Code()
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCode_String(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
code Code
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Success code",
|
||||
code: Success,
|
||||
want: "Success",
|
||||
},
|
||||
{
|
||||
name: "Unschedulable code",
|
||||
code: Unschedulable,
|
||||
want: "Unschedulable",
|
||||
},
|
||||
{
|
||||
name: "Error code",
|
||||
code: Error,
|
||||
want: "Error",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := tt.code.String()
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
Copyright 2024 The Karmada Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
|
||||
)
|
||||
|
||||
func TestNewClusterInfo(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
cluster *clusterv1alpha1.Cluster
|
||||
want *ClusterInfo
|
||||
}{
|
||||
{
|
||||
name: "Create ClusterInfo with valid cluster",
|
||||
cluster: &clusterv1alpha1.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-cluster",
|
||||
},
|
||||
},
|
||||
want: &ClusterInfo{
|
||||
cluster: &clusterv1alpha1.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-cluster",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Create ClusterInfo with nil cluster",
|
||||
cluster: nil,
|
||||
want: &ClusterInfo{cluster: nil},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := NewClusterInfo(tc.cluster)
|
||||
assert.Equal(t, tc.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterInfo_Cluster(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
clusterInfo *ClusterInfo
|
||||
want *clusterv1alpha1.Cluster
|
||||
}{
|
||||
{
|
||||
name: "Get cluster from valid ClusterInfo",
|
||||
clusterInfo: &ClusterInfo{
|
||||
cluster: &clusterv1alpha1.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-cluster",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &clusterv1alpha1.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-cluster",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Get cluster from nil ClusterInfo",
|
||||
clusterInfo: nil,
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := tc.clusterInfo.Cluster()
|
||||
assert.Equal(t, tc.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFitError_Error(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fitError FitError
|
||||
expectedOutput string
|
||||
expectedReasons []string
|
||||
}{
|
||||
{
|
||||
name: "No clusters available",
|
||||
fitError: FitError{
|
||||
NumAllClusters: 0,
|
||||
Diagnosis: Diagnosis{ClusterToResultMap: ClusterToResultMap{}},
|
||||
},
|
||||
expectedOutput: "0/0 clusters are available: no cluster exists.",
|
||||
expectedReasons: []string{},
|
||||
},
|
||||
{
|
||||
name: "Multiple reasons for unavailability",
|
||||
fitError: FitError{
|
||||
NumAllClusters: 3,
|
||||
Diagnosis: Diagnosis{
|
||||
ClusterToResultMap: ClusterToResultMap{
|
||||
"cluster1": &Result{reasons: []string{"insufficient CPU", "insufficient memory"}},
|
||||
"cluster2": &Result{reasons: []string{"insufficient CPU"}},
|
||||
"cluster3": &Result{reasons: []string{"taint mismatch"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedOutput: "0/3 clusters are available:",
|
||||
expectedReasons: []string{
|
||||
"2 insufficient CPU",
|
||||
"1 insufficient memory",
|
||||
"1 taint mismatch",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := tc.fitError.Error()
|
||||
|
||||
// Check if the error message starts with the expected output
|
||||
assert.True(t, strings.HasPrefix(got, tc.expectedOutput), "Error message should start with expected output")
|
||||
|
||||
if len(tc.expectedReasons) > 0 {
|
||||
// Check each reason
|
||||
for _, reason := range tc.expectedReasons {
|
||||
assert.Contains(t, got, reason, "Error message should contain the reason: %s", reason)
|
||||
}
|
||||
|
||||
// Check the total number of reasons
|
||||
gotReasons := strings.Split(strings.TrimPrefix(got, tc.expectedOutput), ",")
|
||||
assert.Equal(t, len(tc.expectedReasons), len(gotReasons), "Number of reasons should match")
|
||||
} else {
|
||||
// If no reasons are expected, the got message should exactly match the expected output
|
||||
assert.Equal(t, tc.expectedOutput, got, "Error message should exactly match expected output when no reasons are provided")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnschedulableError_Error(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
message string
|
||||
}{
|
||||
{
|
||||
name: "Unschedulable due to insufficient resources",
|
||||
message: "Insufficient CPU in all clusters",
|
||||
},
|
||||
{
|
||||
name: "Unschedulable due to taint mismatch",
|
||||
message: "No cluster matches required tolerations",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
unschedulableErr := UnschedulableError{Message: tc.message}
|
||||
assert.Equal(t, tc.message, unschedulableErr.Error())
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
Copyright 2024 The Karmada Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestBindingSchedule(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
scheduleType string
|
||||
duration float64
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Successful schedule",
|
||||
scheduleType: "test",
|
||||
duration: 1.5,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "Failed schedule",
|
||||
scheduleType: "test",
|
||||
duration: 0.5,
|
||||
err: errors.New("schedule failed"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
// We can't easily test the metric values directly, so we'll just ensure the function doesn't panic
|
||||
BindingSchedule(tt.scheduleType, tt.duration, tt.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestScheduleStep(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
action string
|
||||
duration time.Duration
|
||||
}{
|
||||
{
|
||||
name: "Filter step",
|
||||
action: ScheduleStepFilter,
|
||||
duration: 100 * time.Millisecond,
|
||||
},
|
||||
{
|
||||
name: "Score step",
|
||||
action: ScheduleStepScore,
|
||||
duration: 200 * time.Millisecond,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
startTime := time.Now().Add(-tt.duration)
|
||||
// Ensure the function doesn't panic
|
||||
ScheduleStep(tt.action, startTime)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountSchedulerBindings(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
event string
|
||||
}{
|
||||
{
|
||||
name: "Binding add event",
|
||||
event: BindingAdd,
|
||||
},
|
||||
{
|
||||
name: "Binding update event",
|
||||
event: BindingUpdate,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(_ *testing.T) {
|
||||
// Ensure the function doesn't panic
|
||||
CountSchedulerBindings(tt.event)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue