github.com/kubewharf/katalyst-core@v0.5.3/pkg/agent/sysadvisor/metacache/metacache_test.go (about)

     1  /*
     2  Copyright 2022 The Katalyst 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 metacache
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"reflect"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/stretchr/testify/require"
    27  	"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
    28  
    29  	borweinconsts "github.com/kubewharf/katalyst-core/pkg/agent/sysadvisor/plugin/inference/models/borwein/consts"
    30  	"github.com/kubewharf/katalyst-core/pkg/agent/sysadvisor/types"
    31  	"github.com/kubewharf/katalyst-core/pkg/metrics"
    32  )
    33  
    34  func TestMetaCacheImp_GetFilteredInferenceResult(t *testing.T) {
    35  	t.Parallel()
    36  	type fields struct {
    37  		emitter       metrics.MetricEmitter
    38  		modelToResult map[string]interface{}
    39  	}
    40  	type args struct {
    41  		filterFunc func(result interface{}) (interface{}, error)
    42  		modelName  string
    43  	}
    44  	tests := []struct {
    45  		name    string
    46  		fields  fields
    47  		args    args
    48  		want    interface{}
    49  		wantErr bool
    50  	}{
    51  		{
    52  			name: "test without filter",
    53  			fields: fields{
    54  				emitter: metrics.DummyMetrics{},
    55  				modelToResult: map[string]interface{}{
    56  					borweinconsts.ModelNameBorwein: []int{1, 2, 3},
    57  				},
    58  			},
    59  			args: args{
    60  				modelName: borweinconsts.ModelNameBorwein,
    61  			},
    62  			want:    []int{1, 2, 3},
    63  			wantErr: false,
    64  		},
    65  		{
    66  			name: "test with filter",
    67  			fields: fields{
    68  				emitter: metrics.DummyMetrics{},
    69  				modelToResult: map[string]interface{}{
    70  					borweinconsts.ModelNameBorwein: []int{1, 2, 3},
    71  				},
    72  			},
    73  			args: args{
    74  				filterFunc: func(result interface{}) (interface{}, error) {
    75  					parsedResult, ok := result.([]int)
    76  
    77  					if !ok {
    78  						return nil, fmt.Errorf("invalid result")
    79  					}
    80  
    81  					filteredResult := []int{}
    82  
    83  					for _, result := range parsedResult {
    84  						if result < 3 {
    85  							filteredResult = append(filteredResult, result)
    86  						}
    87  					}
    88  
    89  					return filteredResult, nil
    90  				},
    91  				modelName: borweinconsts.ModelNameBorwein,
    92  			},
    93  			want:    []int{1, 2},
    94  			wantErr: false,
    95  		},
    96  		{
    97  			name: "test with invalid result type",
    98  			fields: fields{
    99  				emitter: metrics.DummyMetrics{},
   100  				modelToResult: map[string]interface{}{
   101  					borweinconsts.ModelNameBorwein: []string{"1", "2", "3"},
   102  				},
   103  			},
   104  			args: args{
   105  				filterFunc: func(result interface{}) (interface{}, error) {
   106  					parsedResult, ok := result.([]int)
   107  
   108  					if !ok {
   109  						return nil, fmt.Errorf("invalid result")
   110  					}
   111  
   112  					filteredResult := []int{}
   113  
   114  					for _, result := range parsedResult {
   115  						if result < 3 {
   116  							filteredResult = append(filteredResult, result)
   117  						}
   118  					}
   119  
   120  					return filteredResult, nil
   121  				},
   122  				modelName: borweinconsts.ModelNameBorwein,
   123  			},
   124  			want:    nil,
   125  			wantErr: true,
   126  		},
   127  	}
   128  	for _, tt := range tests {
   129  		tt := tt
   130  		t.Run(tt.name, func(t *testing.T) {
   131  			t.Parallel()
   132  			mc := &MetaCacheImp{
   133  				emitter:       tt.fields.emitter,
   134  				modelToResult: tt.fields.modelToResult,
   135  			}
   136  			got, err := mc.GetFilteredInferenceResult(tt.args.filterFunc, tt.args.modelName)
   137  			if (err != nil) != tt.wantErr {
   138  				t.Errorf("MetaCacheImp.GetFilteredInferenceResult() error = %v, wantErr %v", err, tt.wantErr)
   139  				return
   140  			}
   141  			if !reflect.DeepEqual(got, tt.want) {
   142  				t.Errorf("MetaCacheImp.GetFilteredInferenceResult() = %v, want %v", got, tt.want)
   143  			}
   144  		})
   145  	}
   146  }
   147  
   148  func TestMetaCacheImp_GetInferenceResult(t *testing.T) {
   149  	t.Parallel()
   150  	type fields struct {
   151  		emitter       metrics.MetricEmitter
   152  		modelToResult map[string]interface{}
   153  	}
   154  	type args struct {
   155  		modelName string
   156  	}
   157  	tests := []struct {
   158  		name    string
   159  		fields  fields
   160  		args    args
   161  		want    interface{}
   162  		wantErr bool
   163  	}{
   164  		{
   165  			name: "test get result directly",
   166  			fields: fields{
   167  				emitter: metrics.DummyMetrics{},
   168  				modelToResult: map[string]interface{}{
   169  					borweinconsts.ModelNameBorwein: []int{1, 2, 3},
   170  				},
   171  			},
   172  			args: args{
   173  				modelName: borweinconsts.ModelNameBorwein,
   174  			},
   175  			want:    []int{1, 2, 3},
   176  			wantErr: false,
   177  		},
   178  	}
   179  	for _, tt := range tests {
   180  		tt := tt
   181  		t.Run(tt.name, func(t *testing.T) {
   182  			t.Parallel()
   183  
   184  			mc := &MetaCacheImp{
   185  				emitter:       tt.fields.emitter,
   186  				modelToResult: tt.fields.modelToResult,
   187  			}
   188  			got, err := mc.GetInferenceResult(tt.args.modelName)
   189  			if (err != nil) != tt.wantErr {
   190  				t.Errorf("MetaCacheImp.GetInferenceResult() error = %v, wantErr %v", err, tt.wantErr)
   191  				return
   192  			}
   193  			if !reflect.DeepEqual(got, tt.want) {
   194  				t.Errorf("MetaCacheImp.GetInferenceResult() = %v, want %v", got, tt.want)
   195  			}
   196  		})
   197  	}
   198  }
   199  
   200  func TestMetaCacheImp_SetInferenceResult(t *testing.T) {
   201  	t.Parallel()
   202  	type fields struct {
   203  		emitter       metrics.MetricEmitter
   204  		modelToResult map[string]interface{}
   205  	}
   206  	type args struct {
   207  		modelName string
   208  		result    interface{}
   209  	}
   210  	tests := []struct {
   211  		name    string
   212  		fields  fields
   213  		args    args
   214  		want    interface{}
   215  		wantErr bool
   216  	}{
   217  		{
   218  			name: "get after set",
   219  			fields: fields{
   220  				emitter:       metrics.DummyMetrics{},
   221  				modelToResult: make(map[string]interface{}),
   222  			},
   223  			args: args{
   224  				modelName: borweinconsts.ModelNameBorwein,
   225  				result:    []int{1, 2, 3},
   226  			},
   227  			want:    []int{1, 2, 3},
   228  			wantErr: false,
   229  		},
   230  	}
   231  	for _, tt := range tests {
   232  		tt := tt
   233  		t.Run(tt.name, func(t *testing.T) {
   234  			t.Parallel()
   235  
   236  			mc := &MetaCacheImp{
   237  				emitter:       tt.fields.emitter,
   238  				modelToResult: tt.fields.modelToResult,
   239  			}
   240  
   241  			if err := mc.SetInferenceResult(tt.args.modelName, tt.args.result); (err != nil) != tt.wantErr {
   242  				t.Errorf("MetaCacheImp.SetInferenceResult() error = %v, wantErr %v", err, tt.wantErr)
   243  			}
   244  
   245  			got, err := mc.GetInferenceResult(tt.args.modelName)
   246  			if (err != nil) != tt.wantErr {
   247  				t.Errorf("MetaCacheImp.GetInferenceResult() error = %v, wantErr %v", err, tt.wantErr)
   248  				return
   249  			}
   250  			if !reflect.DeepEqual(got, tt.want) {
   251  				t.Errorf("MetaCacheImp.GetInferenceResult() = %v, want %v", got, tt.want)
   252  			}
   253  		})
   254  	}
   255  }
   256  
   257  func TestRangeAndDeleteContainerWithSafeTime(t *testing.T) {
   258  	t.Parallel()
   259  
   260  	testDir := "/tmp/mc-test-range-delete"
   261  	checkpointManager, err := checkpointmanager.NewCheckpointManager(testDir)
   262  	require.NoError(t, err, "failed to create checkpoint manager")
   263  	defer func() {
   264  		os.RemoveAll(testDir)
   265  	}()
   266  
   267  	mc := &MetaCacheImp{
   268  		podEntries:               map[string]types.ContainerEntries{},
   269  		containerCreateTimestamp: map[string]int64{},
   270  		checkpointManager:        checkpointManager,
   271  		emitter:                  metrics.DummyMetrics{},
   272  		checkpointName:           "test-mc-range-delete",
   273  	}
   274  	ci := &types.ContainerInfo{
   275  		PodUID:        "pod1",
   276  		ContainerName: "c1",
   277  	}
   278  	require.NoError(t, mc.AddContainer("pod1", "c1", ci), "failed to add container")
   279  
   280  	require.NoError(t, mc.RangeAndDeleteContainer(func(containerInfo *types.ContainerInfo) bool {
   281  		return true
   282  	}, 0), "failed to range and delete container without safe time")
   283  	require.Equal(t, 0, len(mc.podEntries), "failed to delete container without safe time")
   284  	require.Equal(t, 0, len(mc.containerCreateTimestamp), "failed to delete container create timestamp without safe time")
   285  
   286  	require.NoError(t, mc.AddContainer("pod1", "c1", ci), "failed to add container")
   287  	require.NoError(t, mc.RangeAndDeleteContainer(func(containerInfo *types.ContainerInfo) bool {
   288  		return true
   289  	}, 1), "failed to skip range and delete container with safe time")
   290  	require.Equal(t, 1, len(mc.podEntries), "failed to protect container with safe time")
   291  	require.Equal(t, 1, len(mc.containerCreateTimestamp), "failed to protect container create timestamp with safe time")
   292  
   293  	require.NoError(t, mc.RangeAndDeleteContainer(func(containerInfo *types.ContainerInfo) bool {
   294  		return true
   295  	}, time.Now().UnixNano()), "failed to skip range and delete container with safe time")
   296  	require.Equal(t, 0, len(mc.podEntries), "failed to delete container before safe time")
   297  	require.Equal(t, 0, len(mc.containerCreateTimestamp), "failed to delete container create timestamp before safe time")
   298  }