...

Source file src/k8s.io/kubectl/pkg/cmd/get/sorter_test.go

Documentation: k8s.io/kubectl/pkg/cmd/get

     1  /*
     2  Copyright 2014 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 get
    18  
    19  import (
    20  	"encoding/json"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  	corev1 "k8s.io/api/core/v1"
    27  	"k8s.io/apimachinery/pkg/api/meta"
    28  	"k8s.io/apimachinery/pkg/api/resource"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    31  	metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
    32  	"k8s.io/apimachinery/pkg/runtime"
    33  	"k8s.io/kubectl/pkg/scheme"
    34  )
    35  
    36  func toUnstructuredOrDie(data []byte) *unstructured.Unstructured {
    37  	unstrBody := map[string]interface{}{}
    38  	err := json.Unmarshal(data, &unstrBody)
    39  	if err != nil {
    40  		panic(err)
    41  	}
    42  	return &unstructured.Unstructured{Object: unstrBody}
    43  }
    44  func encodeOrDie(obj runtime.Object) []byte {
    45  	data, err := runtime.Encode(scheme.Codecs.LegacyCodec(corev1.SchemeGroupVersion), obj)
    46  	if err != nil {
    47  		panic(err.Error())
    48  	}
    49  	return data
    50  }
    51  func createPodSpecResource(t *testing.T, memReq, memLimit, cpuReq, cpuLimit string) corev1.PodSpec {
    52  	t.Helper()
    53  	podSpec := corev1.PodSpec{
    54  		Containers: []corev1.Container{
    55  			{
    56  				Resources: corev1.ResourceRequirements{
    57  					Requests: corev1.ResourceList{},
    58  					Limits:   corev1.ResourceList{},
    59  				},
    60  			},
    61  		},
    62  	}
    63  
    64  	req := podSpec.Containers[0].Resources.Requests
    65  	if memReq != "" {
    66  		memReq, err := resource.ParseQuantity(memReq)
    67  		if err != nil {
    68  			t.Errorf("memory request string is not a valid quantity")
    69  		}
    70  		req["memory"] = memReq
    71  	}
    72  	if cpuReq != "" {
    73  		cpuReq, err := resource.ParseQuantity(cpuReq)
    74  		if err != nil {
    75  			t.Errorf("cpu request string is not a valid quantity")
    76  		}
    77  		req["cpu"] = cpuReq
    78  	}
    79  	limit := podSpec.Containers[0].Resources.Limits
    80  	if memLimit != "" {
    81  		memLimit, err := resource.ParseQuantity(memLimit)
    82  		if err != nil {
    83  			t.Errorf("memory limit string is not a valid quantity")
    84  		}
    85  		limit["memory"] = memLimit
    86  	}
    87  	if cpuLimit != "" {
    88  		cpuLimit, err := resource.ParseQuantity(cpuLimit)
    89  		if err != nil {
    90  			t.Errorf("cpu limit string is not a valid quantity")
    91  		}
    92  		limit["cpu"] = cpuLimit
    93  	}
    94  
    95  	return podSpec
    96  }
    97  func createUnstructuredPodResource(t *testing.T, memReq, memLimit, cpuReq, cpuLimit string) unstructured.Unstructured {
    98  	t.Helper()
    99  	pod := &corev1.Pod{
   100  		Spec: createPodSpecResource(t, memReq, memLimit, cpuReq, cpuLimit),
   101  	}
   102  	return *toUnstructuredOrDie(encodeOrDie(pod))
   103  }
   104  
   105  func TestSortingPrinter(t *testing.T) {
   106  	intPtr := func(val int32) *int32 { return &val }
   107  
   108  	a := &corev1.Pod{
   109  		ObjectMeta: metav1.ObjectMeta{
   110  			Name: "a",
   111  		},
   112  	}
   113  
   114  	b := &corev1.Pod{
   115  		ObjectMeta: metav1.ObjectMeta{
   116  			Name: "b",
   117  		},
   118  	}
   119  
   120  	c := &corev1.Pod{
   121  		ObjectMeta: metav1.ObjectMeta{
   122  			Name: "c",
   123  		},
   124  	}
   125  
   126  	tests := []struct {
   127  		obj         runtime.Object
   128  		sort        runtime.Object
   129  		field       string
   130  		name        string
   131  		expectedErr string
   132  	}{
   133  		{
   134  			name: "empty",
   135  			obj: &corev1.PodList{
   136  				Items: []corev1.Pod{},
   137  			},
   138  			sort: &corev1.PodList{
   139  				Items: []corev1.Pod{},
   140  			},
   141  			field: "{.metadata.name}",
   142  		},
   143  		{
   144  			name: "in-order-already",
   145  			obj: &corev1.PodList{
   146  				Items: []corev1.Pod{
   147  					{
   148  						ObjectMeta: metav1.ObjectMeta{
   149  							Name: "a",
   150  						},
   151  					},
   152  					{
   153  						ObjectMeta: metav1.ObjectMeta{
   154  							Name: "b",
   155  						},
   156  					},
   157  					{
   158  						ObjectMeta: metav1.ObjectMeta{
   159  							Name: "c",
   160  						},
   161  					},
   162  				},
   163  			},
   164  			sort: &corev1.PodList{
   165  				Items: []corev1.Pod{
   166  					{
   167  						ObjectMeta: metav1.ObjectMeta{
   168  							Name: "a",
   169  						},
   170  					},
   171  					{
   172  						ObjectMeta: metav1.ObjectMeta{
   173  							Name: "b",
   174  						},
   175  					},
   176  					{
   177  						ObjectMeta: metav1.ObjectMeta{
   178  							Name: "c",
   179  						},
   180  					},
   181  				},
   182  			},
   183  			field: "{.metadata.name}",
   184  		},
   185  		{
   186  			name: "reverse-order",
   187  			obj: &corev1.PodList{
   188  				Items: []corev1.Pod{
   189  					{
   190  						ObjectMeta: metav1.ObjectMeta{
   191  							Name: "b",
   192  						},
   193  					},
   194  					{
   195  						ObjectMeta: metav1.ObjectMeta{
   196  							Name: "c",
   197  						},
   198  					},
   199  					{
   200  						ObjectMeta: metav1.ObjectMeta{
   201  							Name: "a",
   202  						},
   203  					},
   204  				},
   205  			},
   206  			sort: &corev1.PodList{
   207  				Items: []corev1.Pod{
   208  					{
   209  						ObjectMeta: metav1.ObjectMeta{
   210  							Name: "a",
   211  						},
   212  					},
   213  					{
   214  						ObjectMeta: metav1.ObjectMeta{
   215  							Name: "b",
   216  						},
   217  					},
   218  					{
   219  						ObjectMeta: metav1.ObjectMeta{
   220  							Name: "c",
   221  						},
   222  					},
   223  				},
   224  			},
   225  			field: "{.metadata.name}",
   226  		},
   227  		{
   228  			name: "random-order-timestamp",
   229  			obj: &corev1.PodList{
   230  				Items: []corev1.Pod{
   231  					{
   232  						ObjectMeta: metav1.ObjectMeta{
   233  							CreationTimestamp: metav1.Unix(300, 0),
   234  						},
   235  					},
   236  					{
   237  						ObjectMeta: metav1.ObjectMeta{
   238  							CreationTimestamp: metav1.Unix(100, 0),
   239  						},
   240  					},
   241  					{
   242  						ObjectMeta: metav1.ObjectMeta{
   243  							CreationTimestamp: metav1.Unix(200, 0),
   244  						},
   245  					},
   246  				},
   247  			},
   248  			sort: &corev1.PodList{
   249  				Items: []corev1.Pod{
   250  					{
   251  						ObjectMeta: metav1.ObjectMeta{
   252  							CreationTimestamp: metav1.Unix(100, 0),
   253  						},
   254  					},
   255  					{
   256  						ObjectMeta: metav1.ObjectMeta{
   257  							CreationTimestamp: metav1.Unix(200, 0),
   258  						},
   259  					},
   260  					{
   261  						ObjectMeta: metav1.ObjectMeta{
   262  							CreationTimestamp: metav1.Unix(300, 0),
   263  						},
   264  					},
   265  				},
   266  			},
   267  			field: "{.metadata.creationTimestamp}",
   268  		},
   269  		{
   270  			name: "random-order-numbers",
   271  			obj: &corev1.ReplicationControllerList{
   272  				Items: []corev1.ReplicationController{
   273  					{
   274  						Spec: corev1.ReplicationControllerSpec{
   275  							Replicas: intPtr(5),
   276  						},
   277  					},
   278  					{
   279  						Spec: corev1.ReplicationControllerSpec{
   280  							Replicas: intPtr(1),
   281  						},
   282  					},
   283  					{
   284  						Spec: corev1.ReplicationControllerSpec{
   285  							Replicas: intPtr(9),
   286  						},
   287  					},
   288  				},
   289  			},
   290  			sort: &corev1.ReplicationControllerList{
   291  				Items: []corev1.ReplicationController{
   292  					{
   293  						Spec: corev1.ReplicationControllerSpec{
   294  							Replicas: intPtr(1),
   295  						},
   296  					},
   297  					{
   298  						Spec: corev1.ReplicationControllerSpec{
   299  							Replicas: intPtr(5),
   300  						},
   301  					},
   302  					{
   303  						Spec: corev1.ReplicationControllerSpec{
   304  							Replicas: intPtr(9),
   305  						},
   306  					},
   307  				},
   308  			},
   309  			field: "{.spec.replicas}",
   310  		},
   311  		{
   312  			name: "v1.List in order",
   313  			obj: &corev1.List{
   314  				Items: []runtime.RawExtension{
   315  					{Object: a, Raw: encodeOrDie(a)},
   316  					{Object: b, Raw: encodeOrDie(b)},
   317  					{Object: c, Raw: encodeOrDie(c)},
   318  				},
   319  			},
   320  			sort: &corev1.List{
   321  				Items: []runtime.RawExtension{
   322  					{Object: a, Raw: encodeOrDie(a)},
   323  					{Object: b, Raw: encodeOrDie(b)},
   324  					{Object: c, Raw: encodeOrDie(c)},
   325  				},
   326  			},
   327  			field: "{.metadata.name}",
   328  		},
   329  		{
   330  			name: "v1.List in reverse",
   331  			obj: &corev1.List{
   332  				Items: []runtime.RawExtension{
   333  					{Object: c, Raw: encodeOrDie(c)},
   334  					{Object: b, Raw: encodeOrDie(b)},
   335  					{Object: a, Raw: encodeOrDie(a)},
   336  				},
   337  			},
   338  			sort: &corev1.List{
   339  				Items: []runtime.RawExtension{
   340  					{Object: a, Raw: encodeOrDie(a)},
   341  					{Object: b, Raw: encodeOrDie(b)},
   342  					{Object: c, Raw: encodeOrDie(c)},
   343  				},
   344  			},
   345  			field: "{.metadata.name}",
   346  		},
   347  		{
   348  			name: "some-missing-fields",
   349  			obj: &unstructured.UnstructuredList{
   350  				Object: map[string]interface{}{
   351  					"kind":       "List",
   352  					"apiVersion": "v1",
   353  				},
   354  				Items: []unstructured.Unstructured{
   355  					{
   356  						Object: map[string]interface{}{
   357  							"kind":       "ReplicationController",
   358  							"apiVersion": "v1",
   359  							"status": map[string]interface{}{
   360  								"availableReplicas": 2,
   361  							},
   362  						},
   363  					},
   364  					{
   365  						Object: map[string]interface{}{
   366  							"kind":       "ReplicationController",
   367  							"apiVersion": "v1",
   368  							"status":     map[string]interface{}{},
   369  						},
   370  					},
   371  					{
   372  						Object: map[string]interface{}{
   373  							"kind":       "ReplicationController",
   374  							"apiVersion": "v1",
   375  							"status": map[string]interface{}{
   376  								"availableReplicas": 1,
   377  							},
   378  						},
   379  					},
   380  				},
   381  			},
   382  			sort: &unstructured.UnstructuredList{
   383  				Object: map[string]interface{}{
   384  					"kind":       "List",
   385  					"apiVersion": "v1",
   386  				},
   387  				Items: []unstructured.Unstructured{
   388  					{
   389  						Object: map[string]interface{}{
   390  							"kind":       "ReplicationController",
   391  							"apiVersion": "v1",
   392  							"status":     map[string]interface{}{},
   393  						},
   394  					},
   395  					{
   396  						Object: map[string]interface{}{
   397  							"kind":       "ReplicationController",
   398  							"apiVersion": "v1",
   399  							"status": map[string]interface{}{
   400  								"availableReplicas": 1,
   401  							},
   402  						},
   403  					},
   404  					{
   405  						Object: map[string]interface{}{
   406  							"kind":       "ReplicationController",
   407  							"apiVersion": "v1",
   408  							"status": map[string]interface{}{
   409  								"availableReplicas": 2,
   410  							},
   411  						},
   412  					},
   413  				},
   414  			},
   415  			field: "{.status.availableReplicas}",
   416  		},
   417  		{
   418  			name: "all-missing-fields",
   419  			obj: &unstructured.UnstructuredList{
   420  				Object: map[string]interface{}{
   421  					"kind":       "List",
   422  					"apiVersion": "v1",
   423  				},
   424  				Items: []unstructured.Unstructured{
   425  					{
   426  						Object: map[string]interface{}{
   427  							"kind":       "ReplicationController",
   428  							"apiVersion": "v1",
   429  							"status": map[string]interface{}{
   430  								"replicas": 0,
   431  							},
   432  						},
   433  					},
   434  					{
   435  						Object: map[string]interface{}{
   436  							"kind":       "ReplicationController",
   437  							"apiVersion": "v1",
   438  							"status": map[string]interface{}{
   439  								"replicas": 0,
   440  							},
   441  						},
   442  					},
   443  				},
   444  			},
   445  			field:       "{.status.availableReplicas}",
   446  			expectedErr: "couldn't find any field with path \"{.status.availableReplicas}\" in the list of objects",
   447  		},
   448  		{
   449  			name: "model-invalid-fields",
   450  			obj: &corev1.ReplicationControllerList{
   451  				Items: []corev1.ReplicationController{
   452  					{
   453  						Status: corev1.ReplicationControllerStatus{},
   454  					},
   455  					{
   456  						Status: corev1.ReplicationControllerStatus{},
   457  					},
   458  					{
   459  						Status: corev1.ReplicationControllerStatus{},
   460  					},
   461  				},
   462  			},
   463  			field:       "{.invalid}",
   464  			expectedErr: "couldn't find any field with path \"{.invalid}\" in the list of objects",
   465  		},
   466  		{
   467  			name: "empty fields",
   468  			obj: &corev1.EventList{
   469  				Items: []corev1.Event{
   470  					{
   471  						ObjectMeta:    metav1.ObjectMeta{CreationTimestamp: metav1.Unix(300, 0)},
   472  						LastTimestamp: metav1.Unix(300, 0),
   473  					},
   474  					{
   475  						ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.Unix(200, 0)},
   476  					},
   477  				},
   478  			},
   479  			sort: &corev1.EventList{
   480  				Items: []corev1.Event{
   481  					{
   482  						ObjectMeta: metav1.ObjectMeta{CreationTimestamp: metav1.Unix(200, 0)},
   483  					},
   484  					{
   485  						ObjectMeta:    metav1.ObjectMeta{CreationTimestamp: metav1.Unix(300, 0)},
   486  						LastTimestamp: metav1.Unix(300, 0),
   487  					},
   488  				},
   489  			},
   490  			field: "{.lastTimestamp}",
   491  		},
   492  		{
   493  			name: "pod-resources-cpu-random-order-with-missing-fields",
   494  			obj: &corev1.PodList{
   495  				Items: []corev1.Pod{
   496  					{
   497  						Spec: createPodSpecResource(t, "", "", "0.5", ""),
   498  					},
   499  					{
   500  						Spec: createPodSpecResource(t, "", "", "10", ""),
   501  					},
   502  					{
   503  						Spec: createPodSpecResource(t, "", "", "100m", ""),
   504  					},
   505  					{
   506  						Spec: createPodSpecResource(t, "", "", "", ""),
   507  					},
   508  				},
   509  			},
   510  			sort: &corev1.PodList{
   511  				Items: []corev1.Pod{
   512  					{
   513  						Spec: createPodSpecResource(t, "", "", "", ""),
   514  					},
   515  					{
   516  						Spec: createPodSpecResource(t, "", "", "100m", ""),
   517  					},
   518  					{
   519  						Spec: createPodSpecResource(t, "", "", "0.5", ""),
   520  					},
   521  					{
   522  						Spec: createPodSpecResource(t, "", "", "10", ""),
   523  					},
   524  				},
   525  			},
   526  			field: "{.spec.containers[].resources.requests.cpu}",
   527  		},
   528  		{
   529  			name: "pod-resources-memory-random-order-with-missing-fields",
   530  			obj: &corev1.PodList{
   531  				Items: []corev1.Pod{
   532  					{
   533  						Spec: createPodSpecResource(t, "128Mi", "", "", ""),
   534  					},
   535  					{
   536  						Spec: createPodSpecResource(t, "10Ei", "", "", ""),
   537  					},
   538  					{
   539  						Spec: createPodSpecResource(t, "8Ti", "", "", ""),
   540  					},
   541  					{
   542  						Spec: createPodSpecResource(t, "64Gi", "", "", ""),
   543  					},
   544  					{
   545  						Spec: createPodSpecResource(t, "55Pi", "", "", ""),
   546  					},
   547  					{
   548  						Spec: createPodSpecResource(t, "2Ki", "", "", ""),
   549  					},
   550  					{
   551  						Spec: createPodSpecResource(t, "", "", "", ""),
   552  					},
   553  				},
   554  			},
   555  			sort: &corev1.PodList{
   556  				Items: []corev1.Pod{
   557  					{
   558  						Spec: createPodSpecResource(t, "", "", "", ""),
   559  					},
   560  					{
   561  						Spec: createPodSpecResource(t, "2Ki", "", "", ""),
   562  					},
   563  					{
   564  						Spec: createPodSpecResource(t, "128Mi", "", "", ""),
   565  					},
   566  					{
   567  						Spec: createPodSpecResource(t, "64Gi", "", "", ""),
   568  					},
   569  					{
   570  						Spec: createPodSpecResource(t, "8Ti", "", "", ""),
   571  					},
   572  					{
   573  						Spec: createPodSpecResource(t, "55Pi", "", "", ""),
   574  					},
   575  					{
   576  						Spec: createPodSpecResource(t, "10Ei", "", "", ""),
   577  					},
   578  				},
   579  			},
   580  			field: "{.spec.containers[].resources.requests.memory}",
   581  		},
   582  		{
   583  			name: "pod-unstructured-resources-cpu-random-order-with-missing-fields",
   584  			obj: &unstructured.UnstructuredList{
   585  				Object: map[string]interface{}{
   586  					"kind":       "List",
   587  					"apiVersion": "v1",
   588  				},
   589  				Items: []unstructured.Unstructured{
   590  					createUnstructuredPodResource(t, "", "", "0.5", ""),
   591  					createUnstructuredPodResource(t, "", "", "10", ""),
   592  					createUnstructuredPodResource(t, "", "", "100m", ""),
   593  					createUnstructuredPodResource(t, "", "", "", ""),
   594  				},
   595  			},
   596  			sort: &unstructured.UnstructuredList{
   597  				Object: map[string]interface{}{
   598  					"kind":       "List",
   599  					"apiVersion": "v1",
   600  				},
   601  				Items: []unstructured.Unstructured{
   602  					createUnstructuredPodResource(t, "", "", "", ""),
   603  					createUnstructuredPodResource(t, "", "", "100m", ""),
   604  					createUnstructuredPodResource(t, "", "", "0.5", ""),
   605  					createUnstructuredPodResource(t, "", "", "10", ""),
   606  				},
   607  			},
   608  			field: "{.spec.containers[].resources.requests.cpu}",
   609  		},
   610  		{
   611  			name: "pod-unstructured-resources-memory-random-order-with-missing-fields",
   612  			obj: &unstructured.UnstructuredList{
   613  				Object: map[string]interface{}{
   614  					"kind":       "List",
   615  					"apiVersion": "v1",
   616  				},
   617  				Items: []unstructured.Unstructured{
   618  					createUnstructuredPodResource(t, "128Mi", "", "", ""),
   619  					createUnstructuredPodResource(t, "10Ei", "", "", ""),
   620  					createUnstructuredPodResource(t, "8Ti", "", "", ""),
   621  					createUnstructuredPodResource(t, "64Gi", "", "", ""),
   622  					createUnstructuredPodResource(t, "55Pi", "", "", ""),
   623  					createUnstructuredPodResource(t, "2Ki", "", "", ""),
   624  					createUnstructuredPodResource(t, "", "", "", ""),
   625  				},
   626  			},
   627  			sort: &unstructured.UnstructuredList{
   628  				Object: map[string]interface{}{
   629  					"kind":       "List",
   630  					"apiVersion": "v1",
   631  				},
   632  				Items: []unstructured.Unstructured{
   633  					createUnstructuredPodResource(t, "", "", "", ""),
   634  					createUnstructuredPodResource(t, "2Ki", "", "", ""),
   635  					createUnstructuredPodResource(t, "128Mi", "", "", ""),
   636  					createUnstructuredPodResource(t, "64Gi", "", "", ""),
   637  					createUnstructuredPodResource(t, "8Ti", "", "", ""),
   638  					createUnstructuredPodResource(t, "55Pi", "", "", ""),
   639  					createUnstructuredPodResource(t, "10Ei", "", "", ""),
   640  				},
   641  			},
   642  			field: "{.spec.containers[].resources.requests.memory}",
   643  		},
   644  	}
   645  	for _, tt := range tests {
   646  		t.Run(tt.name+" table", func(t *testing.T) {
   647  			table := &metav1beta1.Table{}
   648  			meta.EachListItem(tt.obj, func(item runtime.Object) error {
   649  				table.Rows = append(table.Rows, metav1beta1.TableRow{
   650  					Object: runtime.RawExtension{Object: toUnstructuredOrDie(encodeOrDie(item))},
   651  				})
   652  				return nil
   653  			})
   654  
   655  			expectedTable := &metav1beta1.Table{}
   656  			meta.EachListItem(tt.sort, func(item runtime.Object) error {
   657  				expectedTable.Rows = append(expectedTable.Rows, metav1beta1.TableRow{
   658  					Object: runtime.RawExtension{Object: toUnstructuredOrDie(encodeOrDie(item))},
   659  				})
   660  				return nil
   661  			})
   662  
   663  			sorter, err := NewTableSorter(table, tt.field)
   664  			if err == nil {
   665  				err = sorter.Sort()
   666  			}
   667  			if err != nil {
   668  				if len(tt.expectedErr) > 0 {
   669  					if strings.Contains(err.Error(), tt.expectedErr) {
   670  						return
   671  					}
   672  					t.Fatalf("%s: expected error containing: %q, got: \"%v\"", tt.name, tt.expectedErr, err)
   673  				}
   674  				t.Fatalf("%s: unexpected error: %v", tt.name, err)
   675  			}
   676  			if len(tt.expectedErr) > 0 {
   677  				t.Fatalf("%s: expected error containing: %q, got none", tt.name, tt.expectedErr)
   678  			}
   679  			if !reflect.DeepEqual(table, expectedTable) {
   680  				t.Errorf("[%s]\nexpected/saw:\n%s", tt.name, cmp.Diff(expectedTable, table))
   681  			}
   682  		})
   683  		t.Run(tt.name, func(t *testing.T) {
   684  			sort := &SortingPrinter{SortField: tt.field, Decoder: scheme.Codecs.UniversalDecoder()}
   685  			err := sort.sortObj(tt.obj)
   686  			if err != nil {
   687  				if len(tt.expectedErr) > 0 {
   688  					if strings.Contains(err.Error(), tt.expectedErr) {
   689  						return
   690  					}
   691  					t.Fatalf("%s: expected error containing: %q, got: \"%v\"", tt.name, tt.expectedErr, err)
   692  				}
   693  				t.Fatalf("%s: unexpected error: %v", tt.name, err)
   694  			}
   695  			if len(tt.expectedErr) > 0 {
   696  				t.Fatalf("%s: expected error containing: %q, got none", tt.name, tt.expectedErr)
   697  			}
   698  			if !reflect.DeepEqual(tt.obj, tt.sort) {
   699  				t.Errorf("[%s]\nexpected:\n%v\nsaw:\n%v", tt.name, tt.sort, tt.obj)
   700  			}
   701  		})
   702  	}
   703  }
   704  
   705  func TestRuntimeSortLess(t *testing.T) {
   706  	var testobj runtime.Object
   707  
   708  	testobj = &corev1.PodList{
   709  		Items: []corev1.Pod{
   710  			{
   711  				ObjectMeta: metav1.ObjectMeta{
   712  					Name: "b",
   713  				},
   714  				Spec: createPodSpecResource(t, "0.5", "", "1Gi", ""),
   715  			},
   716  			{
   717  				ObjectMeta: metav1.ObjectMeta{
   718  					Name: "c",
   719  				},
   720  				Spec: createPodSpecResource(t, "2", "", "1Ti", ""),
   721  			},
   722  			{
   723  				ObjectMeta: metav1.ObjectMeta{
   724  					Name: "a",
   725  				},
   726  				Spec: createPodSpecResource(t, "10m", "", "1Ki", ""),
   727  			},
   728  		},
   729  	}
   730  
   731  	testobjs, err := meta.ExtractList(testobj)
   732  	if err != nil {
   733  		t.Fatalf("ExtractList testobj got unexpected error: %v", err)
   734  	}
   735  
   736  	testfieldName := "{.metadata.name}"
   737  	testruntimeSortName := NewRuntimeSort(testfieldName, testobjs)
   738  
   739  	testfieldCPU := "{.spec.containers[].resources.requests.cpu}"
   740  	testruntimeSortCPU := NewRuntimeSort(testfieldCPU, testobjs)
   741  
   742  	testfieldMemory := "{.spec.containers[].resources.requests.memory}"
   743  	testruntimeSortMemory := NewRuntimeSort(testfieldMemory, testobjs)
   744  
   745  	tests := []struct {
   746  		name         string
   747  		runtimeSort  *RuntimeSort
   748  		i            int
   749  		j            int
   750  		expectResult bool
   751  		expectErr    bool
   752  	}{
   753  		{
   754  			name:         "test name b c less true",
   755  			runtimeSort:  testruntimeSortName,
   756  			i:            0,
   757  			j:            1,
   758  			expectResult: true,
   759  		},
   760  		{
   761  			name:         "test name c a less false",
   762  			runtimeSort:  testruntimeSortName,
   763  			i:            1,
   764  			j:            2,
   765  			expectResult: false,
   766  		},
   767  		{
   768  			name:         "test name b a less false",
   769  			runtimeSort:  testruntimeSortName,
   770  			i:            0,
   771  			j:            2,
   772  			expectResult: false,
   773  		},
   774  		{
   775  			name:         "test cpu 0.5 2 less true",
   776  			runtimeSort:  testruntimeSortCPU,
   777  			i:            0,
   778  			j:            1,
   779  			expectResult: true,
   780  		},
   781  		{
   782  			name:         "test cpu 2 10mi less false",
   783  			runtimeSort:  testruntimeSortCPU,
   784  			i:            1,
   785  			j:            2,
   786  			expectResult: false,
   787  		},
   788  		{
   789  			name:         "test cpu 0.5 10mi less false",
   790  			runtimeSort:  testruntimeSortCPU,
   791  			i:            0,
   792  			j:            2,
   793  			expectResult: false,
   794  		},
   795  		{
   796  			name:         "test memory 1Gi 1Ti less true",
   797  			runtimeSort:  testruntimeSortMemory,
   798  			i:            0,
   799  			j:            1,
   800  			expectResult: true,
   801  		},
   802  		{
   803  			name:         "test memory 1Ti 1Ki less false",
   804  			runtimeSort:  testruntimeSortMemory,
   805  			i:            1,
   806  			j:            2,
   807  			expectResult: false,
   808  		},
   809  		{
   810  			name:         "test memory 1Gi 1Ki less false",
   811  			runtimeSort:  testruntimeSortMemory,
   812  			i:            0,
   813  			j:            2,
   814  			expectResult: false,
   815  		},
   816  	}
   817  
   818  	for i, test := range tests {
   819  		t.Run(test.name, func(t *testing.T) {
   820  			result := test.runtimeSort.Less(test.i, test.j)
   821  			if result != test.expectResult {
   822  				t.Errorf("case[%d]:%s Expected result: %v, Got result: %v", i, test.name, test.expectResult, result)
   823  			}
   824  		})
   825  	}
   826  }
   827  

View as plain text