sigs.k8s.io/kueue@v0.6.2/pkg/util/resource/resource_test.go (about)

     1  /*
     2  Copyright 2023 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 resource
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/google/go-cmp/cmp"
    23  	corev1 "k8s.io/api/core/v1"
    24  	"k8s.io/apimachinery/pkg/api/resource"
    25  )
    26  
    27  func TestMerge(t *testing.T) {
    28  	rl_500mcpu_2GiMem := corev1.ResourceList{
    29  		corev1.ResourceCPU:    resource.MustParse("500m"),
    30  		corev1.ResourceMemory: resource.MustParse("2Gi"),
    31  	}
    32  	rl_1cpu := corev1.ResourceList{
    33  		corev1.ResourceCPU: resource.MustParse("1"),
    34  	}
    35  	rl_1cpu_1GiMem := corev1.ResourceList{
    36  		corev1.ResourceCPU:    resource.MustParse("1"),
    37  		corev1.ResourceMemory: resource.MustParse("1Gi"),
    38  	}
    39  
    40  	type oper_result struct {
    41  		oper   func(a, b corev1.ResourceList) corev1.ResourceList
    42  		result corev1.ResourceList
    43  	}
    44  	cases := map[string]struct {
    45  		a    corev1.ResourceList
    46  		b    corev1.ResourceList
    47  		want map[string]oper_result
    48  	}{
    49  		"asymmetric": {
    50  			a: rl_1cpu,
    51  			b: rl_500mcpu_2GiMem,
    52  			want: map[string]oper_result{
    53  				"merge": {
    54  					oper: MergeResourceListKeepFirst,
    55  					result: corev1.ResourceList{
    56  						corev1.ResourceCPU:    resource.MustParse("1"),
    57  						corev1.ResourceMemory: resource.MustParse("2Gi"),
    58  					},
    59  				},
    60  				"min": {
    61  					oper: MergeResourceListKeepMin,
    62  					result: corev1.ResourceList{
    63  						corev1.ResourceCPU:    resource.MustParse("500m"),
    64  						corev1.ResourceMemory: resource.MustParse("2Gi"),
    65  					},
    66  				},
    67  				"max": {
    68  					oper: MergeResourceListKeepMax,
    69  					result: corev1.ResourceList{
    70  						corev1.ResourceCPU:    resource.MustParse("1"),
    71  						corev1.ResourceMemory: resource.MustParse("2Gi"),
    72  					},
    73  				},
    74  				"sum": {
    75  					oper: MergeResourceListKeepSum,
    76  					result: corev1.ResourceList{
    77  						corev1.ResourceCPU:    resource.MustParse("1500m"),
    78  						corev1.ResourceMemory: resource.MustParse("2Gi"),
    79  					},
    80  				},
    81  			},
    82  		},
    83  		"symmetric": {
    84  			a: rl_1cpu_1GiMem,
    85  			b: rl_500mcpu_2GiMem,
    86  			want: map[string]oper_result{
    87  				"merge": {
    88  					oper: MergeResourceListKeepFirst,
    89  					result: corev1.ResourceList{
    90  						corev1.ResourceCPU:    resource.MustParse("1"),
    91  						corev1.ResourceMemory: resource.MustParse("1Gi"),
    92  					},
    93  				},
    94  				"min": {
    95  					oper: MergeResourceListKeepMin,
    96  					result: corev1.ResourceList{
    97  						corev1.ResourceCPU:    resource.MustParse("500m"),
    98  						corev1.ResourceMemory: resource.MustParse("1Gi"),
    99  					},
   100  				},
   101  				"max": {
   102  					oper: MergeResourceListKeepMax,
   103  					result: corev1.ResourceList{
   104  						corev1.ResourceCPU:    resource.MustParse("1"),
   105  						corev1.ResourceMemory: resource.MustParse("2Gi"),
   106  					},
   107  				},
   108  				"sum": {
   109  					oper: MergeResourceListKeepSum,
   110  					result: corev1.ResourceList{
   111  						corev1.ResourceCPU:    resource.MustParse("1500m"),
   112  						corev1.ResourceMemory: resource.MustParse("3Gi"),
   113  					},
   114  				},
   115  			},
   116  		},
   117  		"nil source": {
   118  			a: rl_1cpu_1GiMem,
   119  			b: nil,
   120  			want: map[string]oper_result{
   121  				"merge": {
   122  					oper:   MergeResourceListKeepFirst,
   123  					result: rl_1cpu_1GiMem,
   124  				},
   125  				"min": {
   126  					oper:   MergeResourceListKeepMin,
   127  					result: rl_1cpu_1GiMem,
   128  				},
   129  				"max": {
   130  					oper:   MergeResourceListKeepMax,
   131  					result: rl_1cpu_1GiMem,
   132  				},
   133  				"sum": {
   134  					oper:   MergeResourceListKeepSum,
   135  					result: rl_1cpu_1GiMem,
   136  				},
   137  			},
   138  		},
   139  		"nil destination": {
   140  			a: nil,
   141  			b: rl_1cpu_1GiMem,
   142  			want: map[string]oper_result{
   143  				"merge": {
   144  					oper:   MergeResourceListKeepFirst,
   145  					result: rl_1cpu_1GiMem,
   146  				},
   147  				"min": {
   148  					oper:   MergeResourceListKeepMin,
   149  					result: rl_1cpu_1GiMem,
   150  				},
   151  				"max": {
   152  					oper:   MergeResourceListKeepMax,
   153  					result: rl_1cpu_1GiMem,
   154  				},
   155  				"sum": {
   156  					oper:   MergeResourceListKeepSum,
   157  					result: rl_1cpu_1GiMem,
   158  				},
   159  			},
   160  		},
   161  		"nil": {
   162  			a: nil,
   163  			b: nil,
   164  			want: map[string]oper_result{
   165  				"merge": {
   166  					oper:   MergeResourceListKeepFirst,
   167  					result: nil,
   168  				},
   169  				"min": {
   170  					oper:   MergeResourceListKeepMin,
   171  					result: nil,
   172  				},
   173  				"max": {
   174  					oper:   MergeResourceListKeepMax,
   175  					result: nil,
   176  				},
   177  				"sum": {
   178  					oper:   MergeResourceListKeepSum,
   179  					result: nil,
   180  				},
   181  			},
   182  		},
   183  	}
   184  
   185  	for name, tc := range cases {
   186  		t.Run(name, func(t *testing.T) {
   187  			for opname, oper := range tc.want {
   188  				t.Run(opname, func(t *testing.T) {
   189  					result := oper.oper(tc.a, tc.b)
   190  					if diff := cmp.Diff(oper.result, result); diff != "" {
   191  						t.Errorf("Unexpected result (-want,+got):\n%s", diff)
   192  					}
   193  				})
   194  			}
   195  		})
   196  	}
   197  
   198  }
   199  
   200  func TestGetGraterKeys(t *testing.T) {
   201  	cpuOnly1 := corev1.ResourceList{
   202  		corev1.ResourceCPU: resource.MustParse("1"),
   203  	}
   204  	cpuOnly500m := corev1.ResourceList{
   205  		corev1.ResourceCPU: resource.MustParse("500m"),
   206  	}
   207  	cases := map[string]struct {
   208  		a, b corev1.ResourceList
   209  		want []string
   210  	}{
   211  		"empty_a": {
   212  			b:    cpuOnly1,
   213  			want: nil,
   214  		},
   215  		"empty_b": {
   216  			a:    cpuOnly1,
   217  			want: nil,
   218  		},
   219  		"less one resource": {
   220  			a:    cpuOnly500m,
   221  			b:    cpuOnly1,
   222  			want: nil,
   223  		},
   224  		"not less one resource": {
   225  			a:    cpuOnly1,
   226  			b:    cpuOnly500m,
   227  			want: []string{corev1.ResourceCPU.String()},
   228  		},
   229  		"multiple unrelated": {
   230  			a: corev1.ResourceList{
   231  				"r1": resource.MustParse("2"),
   232  				"r2": resource.MustParse("2"),
   233  			},
   234  			b: corev1.ResourceList{
   235  				"r3": resource.MustParse("1"),
   236  				"r4": resource.MustParse("1"),
   237  			},
   238  			want: nil,
   239  		},
   240  		"multiple": {
   241  			a: corev1.ResourceList{
   242  				"r1": resource.MustParse("2"),
   243  				"r2": resource.MustParse("1"),
   244  			},
   245  			b: corev1.ResourceList{
   246  				"r1": resource.MustParse("1"),
   247  				"r2": resource.MustParse("2"),
   248  			},
   249  			want: []string{"r1"},
   250  		},
   251  	}
   252  
   253  	for name, tc := range cases {
   254  		t.Run(name, func(t *testing.T) {
   255  			got := GetGreaterKeys(tc.a, tc.b)
   256  			if diff := cmp.Diff(tc.want, got); diff != "" {
   257  				t.Errorf("Unexpected result (-want, +got)\n%s", diff)
   258  			}
   259  		})
   260  	}
   261  }
   262  
   263  func TestQuantityToFloat(t *testing.T) {
   264  	cases := map[string]struct {
   265  		q          resource.Quantity
   266  		wantResult float64
   267  	}{
   268  		"decimal zero exponent": {
   269  			q:          resource.MustParse("5"),
   270  			wantResult: 5,
   271  		},
   272  		"float zero exponent": {
   273  			q:          resource.MustParse("5.5"),
   274  			wantResult: 5.5,
   275  		},
   276  		"decimal positive exponent": {
   277  			q:          resource.MustParse("5k"),
   278  			wantResult: 5000,
   279  		},
   280  		"float positive exponent": {
   281  			q:          resource.MustParse("5.5k"),
   282  			wantResult: 5500,
   283  		},
   284  		"decimal negative exponent": {
   285  			q:          resource.MustParse("5m"),
   286  			wantResult: 0.005,
   287  		},
   288  		"float negative exponent": {
   289  			q:          resource.MustParse("5.5m"),
   290  			wantResult: 0.006, // 1/1000 is the maximum precision, the value will be rounded
   291  		},
   292  	}
   293  
   294  	for name, tc := range cases {
   295  		t.Run(name, func(t *testing.T) {
   296  			got := QuantityToFloat(&tc.q)
   297  			if got != tc.wantResult {
   298  				t.Errorf("Unexpected result, expecting %f got %f", tc.wantResult, got)
   299  			}
   300  		})
   301  	}
   302  }