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 common
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"reflect"
"strconv"
"strings"
"sync"
"syscall"
"testing"
"time"
"github.com/agiledragon/gomonkey/v2"
"github.com/fsnotify/fsnotify"
"github.com/smartystreets/goconvey/convey"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1"
"ascend-common/api"
"ascend-common/common-utils/hwlog"
"ascend-common/common-utils/utils"
)
const (
num3 = 3
num5 = 5
)
type mockFileInfo struct {
mode os.FileMode
sys interface{}
}
func (m *mockFileInfo) Name() string { return "mock" }
func (m *mockFileInfo) Size() int64 { return 0 }
func (m *mockFileInfo) Mode() os.FileMode { return m.mode }
func (m *mockFileInfo) ModTime() time.Time { return time.Now() }
func (m *mockFileInfo) IsDir() bool { return false }
func (m *mockFileInfo) Sys() interface{} { return m.sys }
func init() {
hwLogConfig := hwlog.LogConfig{
OnlyToStdout: true,
}
hwlog.InitRunLogger(&hwLogConfig, context.Background())
}
func TestLockAllDeviceInfo(t *testing.T) {
convey.Convey("test LockAllDeviceInfo", t, func() {
convey.Convey("LockAllDeviceInfo success", func() {
LockAllDeviceInfo()
UnlockAllDeviceInfo()
})
})
}
func TestSetAscendRuntimeEnv(t *testing.T) {
convey.Convey("test SetAscendRuntimeEnv", t, func() {
id := 100
devices := []int{id}
SetAscendRuntimeEnv(devices, "", nil)
ParamOption.RealCardType = api.Ascend310B
resp := v1beta1.ContainerAllocateResponse{}
SetAscendRuntimeEnv(devices, "", &resp)
convey.So(resp.Envs[ascendAllowLinkEnv], convey.ShouldEqual, "True")
convey.So(resp.Envs[AscendVisibleDevicesEnv], convey.ShouldEqual, strconv.Itoa(id))
})
}
func TestMakeDataHash(t *testing.T) {
convey.Convey("test MakeDataHash", t, func() {
convey.Convey("h.Write success", func() {
DeviceInfo := NodeDeviceInfo{DeviceList: map[string]string{HuaweiUnHealthAscend910: "Ascend910-0"}}
ret := MakeDataHash(DeviceInfo)
convey.So(ret, convey.ShouldNotBeEmpty)
})
convey.Convey("json.Marshal failed", func() {
mockMarshal := gomonkey.ApplyFunc(json.Marshal, func(v interface{}) ([]byte, error) {
return nil, fmt.Errorf("err")
})
defer mockMarshal.Reset()
DeviceInfo := NodeDeviceInfo{DeviceList: map[string]string{HuaweiUnHealthAscend910: "Ascend910-0"}}
ret := MakeDataHash(DeviceInfo)
convey.So(ret, convey.ShouldBeEmpty)
})
})
}
func TestMapDeepCopy(t *testing.T) {
convey.Convey("test MapDeepCopy", t, func() {
convey.Convey("input nil", func() {
ret := MapDeepCopy(nil)
convey.So(ret, convey.ShouldNotBeNil)
})
convey.Convey("h.Write success", func() {
devices := map[string]string{"100": DefaultDeviceIP}
ret := MapDeepCopy(devices)
convey.So(len(ret), convey.ShouldEqual, len(devices))
})
})
}
func TestGetDeviceFromPodAnnotation(t *testing.T) {
convey.Convey("test GetDeviceFromPodAnnotation", t, func() {
convey.Convey("input invalid pod", func() {
_, err := GetDeviceFromPodAnnotation(nil, api.Ascend910)
convey.So(err, convey.ShouldNotBeNil)
})
convey.Convey("annotationTag not exist", func() {
_, err := GetDeviceFromPodAnnotation(&v1.Pod{}, api.Ascend910)
convey.So(err, convey.ShouldNotBeNil)
})
convey.Convey("annotationTag exist", func() {
pod := v1.Pod{}
pod.Annotations = map[string]string{api.ResourceNamePrefix + api.Ascend910: "Ascend910-0"}
_, err := GetDeviceFromPodAnnotation(&pod, api.Ascend910)
convey.So(err, convey.ShouldBeNil)
})
})
}
func createFile(filePath string) error {
f, err := os.Create(filePath)
if err != nil {
return err
}
defer f.Close()
if err := f.Chmod(SocketChmod); err != nil {
return err
}
return nil
}
func TestGetDefaultDevices2(t *testing.T) {
convey.Convey("test TestGetDefaultDevices2", t, func() {
mockStat := gomonkey.ApplyFunc(getDavinciManagerPath, func() (string, error) {
return HiAIManagerDevice, nil
})
defer mockStat.Reset()
ParamOption = Option{
ProductTypes: []string{Atlas200ISoc},
}
defer func() {
ParamOption = Option{}
}()
convey.Convey("set200SocDefaultDevices return err", func() {
patch := gomonkey.ApplyFunc(set200SocDefaultDevices, func() ([]string, error) {
return []string{Atlas200ISocVPC}, fmt.Errorf("err")
})
defer patch.Reset()
_, err := GetDefaultDevices(true)
convey.So(err, convey.ShouldNotBeNil)
})
convey.Convey("set200SocDefaultDevices return nil", func() {
patch := gomonkey.ApplyFunc(set200SocDefaultDevices, func() ([]string, error) {
return []string{Atlas200ISocVPC}, nil
})
defer patch.Reset()
_, err := GetDefaultDevices(true)
convey.So(err, convey.ShouldBeNil)
})
})
}
func TestGetDefaultDevices(t *testing.T) {
convey.Convey("pods is nil", t, func() {
mockStat := gomonkey.ApplyFunc(os.Stat, func(name string) (os.FileInfo, error) {
return nil, fmt.Errorf("err")
})
defer mockStat.Reset()
_, err := GetDefaultDevices(true)
convey.So(err, convey.ShouldNotBeNil)
})
if _, err := os.Stat(HiAIHDCDevice); err != nil {
if err = createFile(HiAIHDCDevice); err != nil {
t.Fatal("TestGetDefaultDevices Run Failed")
}
}
if _, err := os.Stat(HiAIManagerDevice); err != nil {
if err = createFile(HiAIManagerDevice); err != nil {
t.Fatal("TestGetDefaultDevices Run Failed")
}
}
if _, err := os.Stat(HiAISVMDevice); err != nil {
if err = createFile(HiAISVMDevice); err != nil {
t.Fatal("TestGetDefaultDevices Run Failed")
}
}
if _, err := os.Stat(DvppCmdlistDevice); err != nil {
if err = createFile(DvppCmdlistDevice); err != nil {
t.Fatal("TestGetDefaultDevices Run Failed")
}
}
defaultDevices, err := GetDefaultDevices(true)
if err != nil {
t.Errorf("TestGetDefaultDevices Run Failed")
}
defaultMap := make(map[string]string)
defaultMap[HiAIHDCDevice] = ""
defaultMap[HiAIManagerDevice] = ""
defaultMap[HiAISVMDevice] = ""
defaultMap[HiAi200RCEventSched] = ""
defaultMap[HiAi200RCHiDvpp] = ""
defaultMap[HiAi200RCLog] = ""
defaultMap[HiAi200RCMemoryBandwidth] = ""
defaultMap[HiAi200RCSVM0] = ""
defaultMap[HiAi200RCTsAisle] = ""
defaultMap[HiAi200RCUpgrade] = ""
defaultMap[DvppCmdlistDevice] = ""
for _, str := range defaultDevices {
if _, ok := defaultMap[str]; !ok {
t.Errorf("TestGetDefaultDevices Run Failed")
}
}
t.Logf("TestGetDefaultDevices Run Pass")
}
func TestSet200SocDefaultDevices(t *testing.T) {
convey.Convey("test set200SocDefaultDevices", t, func() {
convey.Convey("os.Stat err", func() {
mockStat := gomonkey.ApplyFuncReturn(os.Stat, nil, errors.New("failed"))
defer mockStat.Reset()
_, err := set200SocDefaultDevices()
convey.So(err, convey.ShouldNotBeNil)
})
convey.Convey("device is exist", func() {
mockStat := gomonkey.ApplyFunc(os.Stat, func(name string) (os.FileInfo, error) {
if name == HiAi200RCEventSched {
return nil, fmt.Errorf("err")
}
return nil, nil
})
defer mockStat.Reset()
ret, err := set200SocDefaultDevices()
convey.So(ret, convey.ShouldNotBeNil)
convey.So(err, convey.ShouldBeNil)
})
})
}
func TestSet310BDefaultDevices(t *testing.T) {
convey.Convey("test set310BDefaultDevices", t, func() {
convey.Convey("os.Stat err", func() {
mockStat := gomonkey.ApplyFuncReturn(os.Stat, nil, errors.New("failed"))
defer mockStat.Reset()
convey.So(len(set310BDefaultDevices()), convey.ShouldEqual, 0)
})
convey.Convey("device is exist", func() {
mockStat := gomonkey.ApplyFuncReturn(os.Stat, nil, nil)
defer mockStat.Reset()
convey.So(len(set310BDefaultDevices()), convey.ShouldNotEqual, 0)
})
})
}
func TestFilterPods1(t *testing.T) {
convey.Convey("test FilterPods part1", t, func() {
convey.Convey("The number of container exceeds the upper limit", func() {
pods := []v1.Pod{{Spec: v1.PodSpec{Containers: make([]v1.Container, MaxContainerLimit+1)}}}
res := FilterPods(pods, api.Ascend910, nil)
convey.So(res, convey.ShouldBeEmpty)
})
convey.Convey("annotationTag not exist", func() {
pods := []v1.Pod{{Spec: v1.PodSpec{Containers: []v1.Container{{Resources: v1.
ResourceRequirements{Limits: v1.ResourceList{}}}}}}}
res := FilterPods(pods, api.Ascend910, nil)
convey.So(res, convey.ShouldBeEmpty)
})
convey.Convey("annotationTag exist, device is virtual", func() {
limits := resource.NewQuantity(1, resource.DecimalExponent)
pods := []v1.Pod{{Spec: v1.PodSpec{Containers: []v1.Container{{Resources: v1.
ResourceRequirements{Limits: v1.ResourceList{api.ResourceNamePrefix + Ascend910vir2: *limits}}}}}}}
res := FilterPods(pods, Ascend910vir2, nil)
convey.So(len(res), convey.ShouldEqual, 1)
})
convey.Convey("limitsDevNum exceeds the upper limit", func() {
limits := resource.NewQuantity(MaxDevicesNum*MaxAICoreNum+1, resource.DecimalExponent)
pods := []v1.Pod{{Spec: v1.PodSpec{Containers: []v1.Container{{Resources: v1.
ResourceRequirements{Limits: v1.ResourceList{api.ResourceNamePrefix + Ascend910vir2: *limits}}}}}}}
res := FilterPods(pods, Ascend910vir2, nil)
convey.So(res, convey.ShouldBeEmpty)
})
convey.Convey("no assigned flag", func() {
limits := resource.NewQuantity(1, resource.DecimalExponent)
pods := []v1.Pod{
{Spec: v1.PodSpec{Containers: []v1.Container{{Resources: v1.ResourceRequirements{Limits: v1.
ResourceList{api.ResourceNamePrefix + api.Ascend910: *limits}}}}}}}
res := FilterPods(pods, api.Ascend910, nil)
convey.So(res, convey.ShouldBeEmpty)
})
convey.Convey("had assigned flag", func() {
limits := resource.NewQuantity(1, resource.DecimalExponent)
pods := []v1.Pod{
{Spec: v1.PodSpec{Containers: []v1.Container{{Resources: v1.ResourceRequirements{Limits: v1.
ResourceList{api.HuaweiAscend910: *limits}}}}},
ObjectMeta: metav1.ObjectMeta{Name: "test3", Namespace: "test3",
Annotations: map[string]string{
PodPredicateTime: "1", api.HuaweiAscend910: api.Ascend910 + "-1"}},
},
}
res := FilterPods(pods, api.Ascend910, nil)
convey.So(len(res), convey.ShouldEqual, 1)
})
})
}
func TestFilterPods2(t *testing.T) {
convey.Convey("test FilterPods part2", t, func() {
limits := resource.NewQuantity(1, resource.DecimalExponent)
pods := []v1.Pod{
{Spec: v1.PodSpec{Containers: []v1.Container{{Resources: v1.ResourceRequirements{Limits: v1.
ResourceList{api.HuaweiAscend910: *limits}}}}},
ObjectMeta: metav1.ObjectMeta{Name: "test3", Namespace: "test3",
Annotations: map[string]string{
PodPredicateTime: "1", api.HuaweiAscend910: api.Ascend910 + "-1"},
DeletionTimestamp: &metav1.Time{}},
Status: v1.PodStatus{ContainerStatuses: make([]v1.ContainerStatus, 1),
Reason: "UnexpectedAdmissionError"},
},
}
convey.Convey("DeletionTimestamp is not nil", func() {
res := FilterPods(pods, api.Ascend910, nil)
convey.So(res, convey.ShouldBeEmpty)
})
pods[0].DeletionTimestamp = nil
convey.Convey("The number of container status exceeds the upper limit", func() {
pods[0].Status.ContainerStatuses = make([]v1.ContainerStatus, 1)
res := FilterPods(pods, api.Ascend910, nil)
convey.So(res, convey.ShouldBeEmpty)
})
convey.Convey("Waiting.Message is not nil", func() {
pods[0].Status.ContainerStatuses = []v1.ContainerStatus{{State: v1.ContainerState{Waiting: &v1.
ContainerStateWaiting{Message: "PreStartContainer check failed"}}}}
res := FilterPods(pods, api.Ascend910, nil)
convey.So(res, convey.ShouldBeEmpty)
})
convey.Convey("pod.Status.Reason is UnexpectedAdmissionError", func() {
pods[0].Status = v1.PodStatus{ContainerStatuses: []v1.ContainerStatus{},
Reason: "UnexpectedAdmissionError"}
res := FilterPods(pods, api.Ascend910, nil)
convey.So(res, convey.ShouldBeEmpty)
})
convey.Convey("conditionFunc return false", func() {
pods[0].Status = v1.PodStatus{}
mockConitionFunc := func(pod *v1.Pod) bool {
return false
}
res := FilterPods(pods, api.Ascend910, mockConitionFunc)
convey.So(res, convey.ShouldBeEmpty)
})
})
}
func TestVerifyPath(t *testing.T) {
convey.Convey("TestVerifyPath", t, func() {
convey.Convey("filepath.Abs failed", func() {
mock := gomonkey.ApplyFunc(filepath.Abs, func(path string) (string, error) {
return "", fmt.Errorf("err")
})
defer mock.Reset()
_, ret := VerifyPathAndPermission("", 0)
convey.So(ret, convey.ShouldBeFalse)
})
convey.Convey("os.Stat failed", func() {
mock := gomonkey.ApplyFunc(os.Stat, func(name string) (os.FileInfo, error) {
return nil, fmt.Errorf("err")
})
defer mock.Reset()
_, ret := VerifyPathAndPermission("./", 0)
convey.So(ret, convey.ShouldBeFalse)
})
convey.Convey("filepath.EvalSymlinks failed", func() {
mock := gomonkey.ApplyFunc(filepath.EvalSymlinks, func(path string) (string, error) {
return "", fmt.Errorf("err")
})
defer mock.Reset()
_, ret := VerifyPathAndPermission("./", 0)
convey.So(ret, convey.ShouldBeFalse)
})
})
}
func TestCheckPodNameAndSpace(t *testing.T) {
convey.Convey("test CheckPodNameAndSpace", t, func() {
convey.Convey("beyond max length", func() {
podPara, maxLength := "abc", 1
convey.So(CheckPodNameAndSpace(podPara, maxLength), convey.ShouldNotBeNil)
})
convey.Convey("device is exist", func() {
podPara, maxLength := "abc", PodNameMaxLength
convey.So(CheckPodNameAndSpace(podPara, maxLength), convey.ShouldBeNil)
podPara = "abc_d"
convey.So(CheckPodNameAndSpace(podPara, maxLength), convey.ShouldNotBeNil)
})
})
}
func TestWatchFile(t *testing.T) {
convey.Convey("TestWatchFile", t, func() {
convey.Convey("fsnotify.NewWatcher ok", func() {
watcher, err := NewFileWatch()
convey.So(err, convey.ShouldBeNil)
convey.So(watcher, convey.ShouldNotBeNil)
})
convey.Convey("fsnotify.NewWatcher failed", func() {
mock := gomonkey.ApplyFunc(fsnotify.NewWatcher, func() (*fsnotify.Watcher, error) {
return nil, fmt.Errorf("error")
})
defer mock.Reset()
watcher, err := NewFileWatch()
convey.So(err, convey.ShouldNotBeNil)
convey.So(watcher, convey.ShouldBeNil)
})
watcher, _ := NewFileWatch()
convey.Convey("stat failed", func() {
convey.So(watcher, convey.ShouldNotBeNil)
mock := gomonkey.ApplyFunc(os.Stat, func(name string) (os.FileInfo, error) {
return nil, fmt.Errorf("err")
})
defer mock.Reset()
err := watcher.WatchFile("")
convey.So(err, convey.ShouldNotBeNil)
})
mockStat := gomonkey.ApplyFunc(os.Stat, func(name string) (os.FileInfo, error) {
return nil, nil
})
defer mockStat.Reset()
convey.Convey("Add failed", func() {
convey.So(watcher, convey.ShouldNotBeNil)
mockWatchFile := gomonkey.ApplyMethod(reflect.TypeOf(new(fsnotify.Watcher)), "Add",
func(_ *fsnotify.Watcher, name string) error { return fmt.Errorf("err") })
defer mockWatchFile.Reset()
err := watcher.WatchFile("")
convey.So(err, convey.ShouldNotBeNil)
})
convey.Convey("Add ok", func() {
convey.So(watcher, convey.ShouldNotBeNil)
mockWatchFile := gomonkey.ApplyMethod(reflect.TypeOf(new(fsnotify.Watcher)), "Add",
func(_ *fsnotify.Watcher, name string) error { return nil })
defer mockWatchFile.Reset()
err := watcher.WatchFile("")
convey.So(err, convey.ShouldBeNil)
})
})
}
func TestGetDeviceListID(t *testing.T) {
convey.Convey("TestGetDeviceListID", t, func() {
convey.Convey("device num excceed max num", func() {
devices := make([]string, MaxDevicesNum+1)
_, _, ret := GetDeviceListID(devices, "")
convey.So(ret, convey.ShouldNotBeNil)
})
convey.Convey("device name is invalid", func() {
devices := []string{"Ascend910"}
_, _, ret := GetDeviceListID(devices, "")
convey.So(ret, convey.ShouldNotBeNil)
})
convey.Convey("physical device", func() {
devices := []string{"Ascend910-0"}
_, ascendVisibleDevices, ret := GetDeviceListID(devices, "")
convey.So(ret, convey.ShouldBeNil)
convey.So(len(ascendVisibleDevices), convey.ShouldEqual, 1)
})
convey.Convey("virtual device", func() {
devices := []string{"Ascend910-2c-100-0"}
_, ascendVisibleDevices, ret := GetDeviceListID(devices, VirtualDev)
convey.So(ret, convey.ShouldBeNil)
convey.So(len(ascendVisibleDevices), convey.ShouldEqual, 1)
})
})
}
func TestGetPodConfiguration(t *testing.T) {
convey.Convey("TestGetPodConfiguration", t, func() {
convey.Convey("Marshal failed", func() {
mockMarshal := gomonkey.ApplyFunc(json.Marshal, func(v interface{}) ([]byte, error) {
return nil, fmt.Errorf("err")
})
defer mockMarshal.Reset()
devices := map[int]string{100: DefaultDeviceIP}
phyDevMapVirtualDev := map[int]int{100: 0}
deviceType := "Ascend910-2c"
superPodID := int32(1)
info := ServerInfo{
ServerID: DefaultDeviceIP,
DeviceType: deviceType,
SuperPodID: superPodID,
}
ret := GetPodConfiguration(phyDevMapVirtualDev, devices, "pod-name", info, nil)
convey.So(ret, convey.ShouldBeEmpty)
})
convey.Convey("Marshal ok", func() {
devices := map[int]string{100: DefaultDeviceIP}
phyDevMapVirtualDev := map[int]int{100: 0}
deviceType := "Ascend910-2c"
superPodID := int32(1)
info := ServerInfo{
ServerID: DefaultDeviceIP,
DeviceType: deviceType,
SuperPodID: superPodID,
}
ret := GetPodConfiguration(phyDevMapVirtualDev, devices, "pod-name", info, nil)
convey.So(ret, convey.ShouldNotBeEmpty)
})
})
}
func TestNewSignWatcher(t *testing.T) {
convey.Convey("TestNewSignWatcher", t, func() {
signChan := NewSignWatcher(syscall.SIGHUP)
convey.So(signChan, convey.ShouldNotBeNil)
})
}
func TestCheckFileUserSameWithProcess(t *testing.T) {
convey.Convey("CheckFileUserSameWithProcess", t, func() {
var testMode os.FileMode = 0660
loggerPath := "/home/test"
convey.Convey("user is root", func() {
mockFunc := gomonkey.ApplyFuncReturn(os.Getuid, RootUID)
defer mockFunc.Reset()
convey.So(CheckFileUserSameWithProcess(loggerPath), convey.ShouldBeTrue)
})
convey.Convey("user is not root, logger path is unavailable", func() {
mockFunc := gomonkey.ApplyFuncReturn(os.Getuid, 1).
ApplyFuncReturn(os.Lstat, &mockFileInfo{}, fmt.Errorf("get path stat failed"))
defer mockFunc.Reset()
convey.So(CheckFileUserSameWithProcess(loggerPath), convey.ShouldBeFalse)
})
convey.Convey("user is not root, logger file is unavailable", func() {
mockFunc := gomonkey.ApplyFuncReturn(os.Getuid, 1).
ApplyFuncReturn(os.Lstat, &mockFileInfo{mode: testMode, sys: "invalid-type"}, nil)
defer mockFunc.Reset()
convey.So(CheckFileUserSameWithProcess(loggerPath), convey.ShouldBeFalse)
})
convey.Convey("user is not root, logger file stat uid or gid is not equal curUid", func() {
mockFunc := gomonkey.ApplyFuncReturn(os.Getuid, 1).
ApplyFuncReturn(os.Lstat, &mockFileInfo{mode: testMode, sys: &syscall.Stat_t{Uid: RootUID}}, nil)
defer mockFunc.Reset()
convey.So(CheckFileUserSameWithProcess(loggerPath), convey.ShouldBeFalse)
})
convey.Convey("user is not root, both logger file stat uid and gid are equal curUid", func() {
mockFunc := gomonkey.ApplyFuncReturn(os.Getuid, 1).
ApplyFuncReturn(os.Lstat, &mockFileInfo{mode: testMode, sys: &syscall.Stat_t{Uid: 1, Gid: 1}}, nil)
defer mockFunc.Reset()
convey.So(CheckFileUserSameWithProcess(loggerPath), convey.ShouldBeTrue)
})
})
}
func TestIsContainAtlas300IDuo(t *testing.T) {
convey.Convey("IsContainAtlas300IDuo", t, func() {
convey.Convey("IsContainAtlas300IDuo success", func() {
ParamOption.ProductTypes = nil
convey.So(IsContainAtlas300IDuo(), convey.ShouldBeFalse)
ParamOption.ProductTypes = []string{Atlas300IDuo}
convey.So(IsContainAtlas300IDuo(), convey.ShouldBeTrue)
})
})
}
func TestRecordFaultInfoList(t *testing.T) {
convey.Convey("test RecordFaultInfoList success", t, func() {
RecordFaultInfoList([]*TaskDevInfo{{}})
})
}
func TestInt32Join(t *testing.T) {
convey.Convey("test Int32Join success", t, func() {
convey.So(Int32Join([]int32{0, 1}, ","), convey.ShouldEqual, "0,1")
})
}
func TestGetDeviceRunMode(t *testing.T) {
convey.Convey("test GetDeviceRunMode", t, func() {
convey.Convey("device mode is Ascend310", func() {
ParamOption.RealCardType = api.Ascend310
ret, err := GetDeviceRunMode()
convey.So(ret, convey.ShouldEqual, api.Ascend310)
convey.So(err, convey.ShouldBeNil)
})
convey.Convey("device mode is Ascend910, when card real type is Ascend910", func() {
ParamOption.RealCardType = api.Ascend910
ret, err := GetDeviceRunMode()
convey.So(ret, convey.ShouldEqual, api.Ascend910)
convey.So(err, convey.ShouldBeNil)
})
convey.Convey("device mode is Ascend910, when card real type is Ascend910B", func() {
ParamOption.RealCardType = api.Ascend910B
ret, err := GetDeviceRunMode()
convey.So(ret, convey.ShouldEqual, api.Ascend910)
convey.So(err, convey.ShouldBeNil)
})
convey.Convey("device mode is Ascend910, when card real type is Atlas A3", func() {
ParamOption.RealCardType = api.Ascend910A3
ret, err := GetDeviceRunMode()
convey.So(ret, convey.ShouldEqual, api.Ascend910)
convey.So(err, convey.ShouldBeNil)
})
convey.Convey("device mode is Ascend310P", func() {
ParamOption.RealCardType = api.Ascend310P
ret, err := GetDeviceRunMode()
convey.So(ret, convey.ShouldEqual, api.Ascend310P)
convey.So(err, convey.ShouldBeNil)
})
convey.Convey("device mode is invalid", func() {
ParamOption.RealCardType = ""
_, err := GetDeviceRunMode()
convey.So(err, convey.ShouldNotBeNil)
})
})
}
func TestCheckDeviceName(t *testing.T) {
originalRealCardType := ParamOption.RealCardType
defer func() { ParamOption.RealCardType = originalRealCardType }()
convey.Convey("test CheckDeviceName", t, func() {
convey.Convey("device name is valid", func() {
convey.So(CheckDeviceName(api.Ascend910+"-0", api.Ascend910), convey.ShouldBeTrue)
})
convey.Convey("device name is invalid", func() {
convey.So(CheckDeviceName("", api.Ascend910), convey.ShouldBeFalse)
})
convey.Convey("npu-x format should be valid for Atlas 950", func() {
ParamOption.RealCardType = api.Ascend910A5
convey.So(CheckDeviceName(api.NPULowerCase+"-0", api.Ascend910), convey.ShouldBeTrue)
convey.So(CheckDeviceName(api.NPULowerCase+"-3", api.Ascend910), convey.ShouldBeTrue)
})
convey.Convey("Ascend910-x format should still be valid for Atlas 950", func() {
ParamOption.RealCardType = api.Ascend910A5
convey.So(CheckDeviceName(api.Ascend910+"-0", api.Ascend910), convey.ShouldBeTrue)
})
convey.Convey("npu-x format should be invalid for old device type", func() {
ParamOption.RealCardType = api.Ascend910A
convey.So(CheckDeviceName(api.NPULowerCase+"-0", api.Ascend910), convey.ShouldBeFalse)
})
})
}
func newTestPod(labels map[string]string) *v1.Pod {
return &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
}
}
func TestGetJobNameOfPod(t *testing.T) {
convey.Convey("Test GetJobNameOfPod function", t, func() {
const fakeJobName = "job1"
convey.Convey("Pod pointer is nil", func() {
res := GetJobNameOfPod(nil)
convey.So(res, convey.ShouldEqual, "")
})
convey.Convey("Label ResetTaskNameKey exists (highest priority)", func() {
pod := newTestPod(map[string]string{ResetTaskNameKey: fakeJobName})
res := GetJobNameOfPod(pod)
convey.So(res, convey.ShouldEqual, fakeJobName)
})
convey.Convey("Label ResetTaskNameKeyInLabel exists (second priority)", func() {
pod := newTestPod(map[string]string{ResetTaskNameKeyInLabel: fakeJobName})
res := GetJobNameOfPod(pod)
convey.So(res, convey.ShouldEqual, fakeJobName)
})
convey.Convey("Label DeploymentNameKeyInLabel exists (lowest priority)", func() {
pod := newTestPod(map[string]string{DeploymentNameKeyInLabel: fakeJobName})
res := GetJobNameOfPod(pod)
convey.So(res, convey.ShouldEqual, fakeJobName)
})
convey.Convey("Multiple target labels exist, verify priority rule", func() {
pod := newTestPod(map[string]string{
ResetTaskNameKey: fakeJobName,
ResetTaskNameKeyInLabel: "other-job",
DeploymentNameKeyInLabel: "other-job2",
})
res := GetJobNameOfPod(pod)
convey.So(res, convey.ShouldEqual, fakeJobName)
})
convey.Convey("No target labels exist, return empty string", func() {
pod := newTestPod(map[string]string{"other-label": "val"})
res := GetJobNameOfPod(pod)
convey.So(res, convey.ShouldEqual, "")
})
convey.Convey("Pod has empty label map, return empty string", func() {
pod := newTestPod(map[string]string{})
res := GetJobNameOfPod(pod)
convey.So(res, convey.ShouldEqual, "")
})
})
}
func TestGetSyncMapLen(t *testing.T) {
t.Run("TestGetSyncMapLen", func(t *testing.T) {
m := &sync.Map{}
m.Store("key1", "value1")
m.Store("key2", "value2")
if got := GetSyncMapLen(m); got != 2 {
t.Errorf("GetSyncMapLen() = %v, want %v", got, 2)
}
})
}
func TestObjToString(t *testing.T) {
t.Run("TestObjToString", func(t *testing.T) {
mp := map[string]string{"hello": "world"}
want := `{"hello":"world"}`
if got := ObjToString(mp); got != want {
t.Errorf("ObjToString failed")
}
})
}
func TestKeys(t *testing.T) {
t.Run("TestKeys", func(t *testing.T) {
mp := map[string]string{"key": "value"}
want := "key"
if got := Keys(mp); got[0] != want {
t.Errorf("Keys() = %v, want %v", got[0], want)
}
})
}
func TestSetDeviceByPathWhen200RC(t *testing.T) {
convey.Convey("test setDeviceByPathWhen200RC", t, func() {
mockStat := gomonkey.ApplyFunc(os.Stat, func(name string) (os.FileInfo, error) {
return nil, nil
})
defer mockStat.Reset()
devices := []string{}
setDeviceByPathWhen200RC(&devices)
convey.So(len(devices), convey.ShouldEqual, 7)
convey.So(devices[0], convey.ShouldEqual, HiAi200RCEventSched)
if len(devices) >= 7 {
convey.So(devices[6], convey.ShouldEqual, HiAi200RCUpgrade)
}
})
}
func TestGetPodNameFromEnv(t *testing.T) {
convey.Convey("test getPodNameFromEnv", t, func() {
convey.Convey("01-check pod name and space failed, should return error", func() {
mockGetEnv := gomonkey.ApplyFuncReturn(os.Getenv, "master-1")
defer mockGetEnv.Reset()
mockCheck := gomonkey.ApplyFuncReturn(CheckPodNameAndSpace, errors.New("failed"))
defer mockCheck.Reset()
_, err := GetPodNameFromEnv()
convey.So(err, convey.ShouldNotBeNil)
})
convey.Convey("02-check pod name and space success, should return nil", func() {
mockGetEnv := gomonkey.ApplyFuncReturn(os.Getenv, "master-1")
defer mockGetEnv.Reset()
podName, err := GetPodNameFromEnv()
convey.So(err, convey.ShouldBeNil)
convey.So(podName == "master-1", convey.ShouldBeTrue)
})
})
}
func TestIsContainAll300IDuo(t *testing.T) {
convey.Convey("test IsContainAll300IDuo", t, func() {
convey.Convey("01-productType length is zero, should return false", func() {
mockParam := gomonkey.ApplyGlobalVar(&ParamOption, Option{})
defer mockParam.Reset()
convey.So(IsContainAll300IDuo(), convey.ShouldBeFalse)
})
convey.Convey("02-productType has Atlas300IDuo, should return true", func() {
mockParam := gomonkey.ApplyGlobalVar(&ParamOption, Option{ProductTypes: []string{Atlas300IDuo}})
defer mockParam.Reset()
convey.So(IsContainAll300IDuo(), convey.ShouldBeTrue)
})
convey.Convey("03-productType has other type, should return false", func() {
mockParam := gomonkey.ApplyGlobalVar(&ParamOption, Option{ProductTypes: []string{Atlas300IDuo, "other type"}})
defer mockParam.Reset()
convey.So(IsContainAll300IDuo(), convey.ShouldBeFalse)
})
})
}
func TestIntInList(t *testing.T) {
convey.Convey("test intInList", t, func() {
list := []int32{1, 2, 3}
convey.So(IntInList(2, list), convey.ShouldBeTrue)
convey.So(IntInList(4, list), convey.ShouldBeFalse)
})
}
func TestCompareStringSetMap(t *testing.T) {
tests := []struct {
name string
map1 map[string]sets.String
map2 map[string]sets.String
expected bool
}{
{name: "Both maps are nil, should return true", map1: nil, map2: nil, expected: true},
{name: "One map is nil and the other is not, should return false",
map1: nil, map2: map[string]sets.String{}, expected: false},
{name: "Maps have different lengths, should return false",
map1: map[string]sets.String{"key1": sets.NewString("subKey1")},
map2: map[string]sets.String{"key1": sets.NewString("subKey1"),
"key2": sets.NewString("subKey1")}, expected: false},
{name: "Maps have different keys, should return false",
map1: map[string]sets.String{"key1": sets.NewString("subKey1")},
map2: map[string]sets.String{"key2": sets.NewString("subKey1")}, expected: false},
{name: "Maps have different values, should return false",
map1: map[string]sets.String{"key": sets.NewString("subKey1")},
map2: map[string]sets.String{"key": sets.NewString("subKey2")}, expected: false},
{name: "Maps are identical, should return true",
map1: map[string]sets.String{"key": sets.NewString("subKey")},
map2: map[string]sets.String{"key": sets.NewString("subKey")}, expected: true},
{name: "Maps have same keys and values but different order, should return true",
map1: map[string]sets.String{"key": sets.NewString("subKey1", "subKey2")},
map2: map[string]sets.String{"key": sets.NewString("subKey2", "subKey1")}, expected: true},
{name: "Both maps are empty, should return true",
map1: map[string]sets.String{}, map2: map[string]sets.String{}, expected: true},
}
convey.Convey("Test CompareStringSetMap function", t, func() {
for _, tc := range tests {
convey.Convey(tc.name, func() {
res := CompareStringSetMap(tc.map1, tc.map2)
convey.So(res, convey.ShouldEqual, tc.expected)
})
}
})
}
func TestGetDeviceFromAllDeviceInfo(t *testing.T) {
convey.Convey("test getDeviceFromAllDeviceInfo", t, func() {
convey.Convey("01. should return device for given device Id and allDevices", func() {
deviceId := 0
devices := map[int]string{0: "DefaultDeviceIP"}
allDevs := []NpuDevice{
{
SuperDeviceID: 1,
LevelList: []api.RankLevel{
{Level: 0, Info: make(map[string]api.LevelElement)},
},
},
}
dev := getDeviceFromAllDeviceInfo(deviceId, devices, allDevs)
convey.So(reflect.DeepEqual(dev, Device{
DeviceID: "0",
DeviceIP: "DefaultDeviceIP",
SuperDeviceID: "1",
LevelList: []api.RankLevel{
{Level: 0, Info: make(map[string]api.LevelElement)},
},
}), convey.ShouldBeTrue)
})
convey.Convey("02. should return abnormal device when allDevices contains no device Id", func() {
deviceId := 0
devices := map[int]string{0: "DefaultDeviceIP"}
dev := getDeviceFromAllDeviceInfo(deviceId, devices, nil)
convey.So(dev.SuperDeviceID, convey.ShouldEqual, strconv.Itoa(SdIdAbnormal))
convey.So(dev.LevelList, convey.ShouldBeNil)
})
})
}
func TestTriggerUpdate(t *testing.T) {
convey.Convey("trigger update success", t, func() {
verifyUpdateTrigger(t)
TriggerUpdate("test trigger update")
convey.So(verifyUpdateTrigger(t), convey.ShouldBeTrue)
})
convey.Convey("not trigger update", t, func() {
verifyUpdateTrigger(t)
if updateTriggerChan == nil {
t.Error("updateTriggerChan is nil")
}
updateTriggerChan <- struct{}{}
TriggerUpdate("test trigger update")
convey.So(verifyUpdateTrigger(t), convey.ShouldBeTrue)
})
}
func verifyUpdateTrigger(t *testing.T) bool {
if updateTriggerChan == nil {
t.Error("updateTriggerChan is nil")
}
select {
case <-updateTriggerChan:
return true
default:
return false
}
}
type testCase struct {
name string
pod *v1.Pod
deviceType string
expectedAnnot string
expectedErr string
}
func getAnnotationTag(deviceType string) string {
return fmt.Sprintf("%s%s", api.ResourceNamePrefix, deviceType)
}
func createNilPodTestCase() testCase {
return testCase{
name: "Pod is nil",
pod: nil,
deviceType: "gpu",
expectedAnnot: "",
expectedErr: "invalid pod",
}
}
func createAnnotationNotFoundTestCase() testCase {
return testCase{
name: "Annotation not found",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Annotations: map[string]string{
"other-annotation": "some-value",
},
},
},
deviceType: "gpu",
expectedAnnot: "",
expectedErr: "cannot find the annotation",
}
}
func createAnnotationTooLongTestCase() testCase {
return testCase{
name: "Annotation too long",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Annotations: map[string]string{
getAnnotationTag("gpu"): strings.Repeat("a", PodAnnotationMaxLength+1),
},
},
},
deviceType: "gpu",
expectedAnnot: "",
expectedErr: "pod annotation size out of memory",
}
}
func createSuccessfulGetAnnotationTestCase() testCase {
return testCase{
name: "Successfully get annotation",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Annotations: map[string]string{
getAnnotationTag("gpu"): "gpu-resource-info",
"other-annotation": "other-value",
},
},
},
deviceType: "gpu",
expectedAnnot: "gpu-resource-info",
expectedErr: "",
}
}
func TestGetPodAnnotationByDeviceType(t *testing.T) {
convey.Convey("Testing GetPodAnnotationByDeviceType function", t, func() {
testCases := []testCase{
createNilPodTestCase(),
createAnnotationNotFoundTestCase(),
createAnnotationTooLongTestCase(),
createSuccessfulGetAnnotationTestCase(),
}
for _, tc := range testCases {
convey.Convey(tc.name, func() {
annot, err := GetPodAnnotationByDeviceType(tc.pod, tc.deviceType)
convey.So(tc.expectedAnnot, convey.ShouldEqual, annot)
if tc.expectedErr != "" {
convey.So(err, convey.ShouldNotBeNil)
} else {
convey.So(err, convey.ShouldBeNil)
}
})
}
})
}
type isSoftShareDevJobTestCase struct {
name string
pod *v1.Pod
expected bool
}
func createIsSoftShareDevJobTestCase() []isSoftShareDevJobTestCase {
return []isSoftShareDevJobTestCase{
{
name: "Pod is nil",
pod: nil,
expected: false,
},
{
name: "Pod.Annotations is nil",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: nil,
},
},
expected: false,
},
{
name: "Missing SchedulerSoftShareDevPolicyKey",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
api.SchedulerSoftShareDevAicoreQuotaKey: "10",
api.SchedulerSoftShareDevHbmQuotaKey: "20",
},
},
},
expected: false,
},
{
name: "All required keys present",
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
api.SchedulerSoftShareDevPolicyKey: "policy-value",
api.SchedulerSoftShareDevAicoreQuotaKey: "10",
api.SchedulerSoftShareDevHbmQuotaKey: "20",
},
},
},
expected: true,
},
}
}
func TestIsSoftShareDevJob(t *testing.T) {
tests := createIsSoftShareDevJobTestCase()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := IsSoftShareDevJob(tt.pod)
if result != tt.expected {
t.Errorf("IsSoftShareDevJob() = %v, want %v", result, tt.expected)
}
})
}
}
func TestConvertSchedulingPolicyToIntStr(t *testing.T) {
convey.Convey("Testing ConvertSchedulingPolicyToIntStr function", t, func() {
testCases := []struct {
name string
input string
expectedOutput string
}{
{
name: "TC001 - Empty input",
input: "",
expectedOutput: "",
},
{
name: "TC002 - Valid input 'fixed-share'",
input: "fixed-share",
expectedOutput: "1",
},
{
name: "TC003 - Valid input 'elastic'",
input: "elastic",
expectedOutput: "2",
},
{
name: "TC004 - Valid input 'best-effort'",
input: "best-effort",
expectedOutput: "3",
},
{
name: "TC005 - Invalid input 'unknown-policy'",
input: "unknown-policy",
expectedOutput: "",
},
}
for _, tc := range testCases {
convey.Convey(tc.name, func() {
result := ConvertSchedulingPolicyToIntStr(tc.input)
convey.So(tc.expectedOutput, convey.ShouldEqual, result)
})
}
})
}
type getDeviceFromPodAnnotationTestCase struct {
name string
mockRealCardType string
mockStatDockerPath func() (os.FileInfo, error)
mockStatDevicePath func() (os.FileInfo, error)
expectedPath string
expectedErr error
}
func buildGetDeviceFromPodAnnotationTestCases() []getDeviceFromPodAnnotationTestCase {
testErrFileNotExist := errors.New("stat /dev/hiai_manager: no such file or directory")
return []getDeviceFromPodAnnotationTestCase{
{
name: "Ascend310B card + docker path exists",
mockRealCardType: api.Ascend310B,
mockStatDockerPath: func() (os.FileInfo, error) { return nil, nil },
mockStatDevicePath: func() (os.FileInfo, error) { return nil, nil },
expectedPath: HiAIManagerDeviceDocker,
expectedErr: nil,
},
{
name: "Ascend310B card + docker path not exist + device path exists",
mockRealCardType: api.Ascend310B,
mockStatDockerPath: func() (os.FileInfo, error) { return nil, testErrFileNotExist },
mockStatDevicePath: func() (os.FileInfo, error) { return nil, nil },
expectedPath: HiAIManagerDevice,
expectedErr: nil,
},
{
name: "non-Ascend310B card + device path exists",
mockRealCardType: api.Ascend910,
mockStatDockerPath: func() (os.FileInfo, error) { return nil, nil },
mockStatDevicePath: func() (os.FileInfo, error) { return nil, nil },
expectedPath: HiAIManagerDevice,
expectedErr: nil,
},
{
name: "device path not exist",
mockRealCardType: api.Ascend910,
mockStatDockerPath: func() (os.FileInfo, error) { return nil, nil },
mockStatDevicePath: func() (os.FileInfo, error) { return nil, testErrFileNotExist },
expectedPath: "",
expectedErr: testErrFileNotExist,
},
}
}
func TestGetDavinciManagerPath(t *testing.T) {
convey.Convey("Test getDavinciManagerPath", t, func() {
tests := buildGetDeviceFromPodAnnotationTestCases()
for _, tt := range tests {
convey.Convey(tt.name, func() {
patchCardType := gomonkey.ApplyGlobalVar(&ParamOption, Option{RealCardType: tt.mockRealCardType})
defer patchCardType.Reset()
patchStat := gomonkey.ApplyFunc(os.Stat,
func(path string) (os.FileInfo, error) {
switch path {
case HiAIManagerDeviceDocker:
return tt.mockStatDockerPath()
case HiAIManagerDevice:
return tt.mockStatDevicePath()
default:
return nil, os.ErrNotExist
}
})
defer patchStat.Reset()
gotPath, gotErr := getDavinciManagerPath()
convey.So(gotPath, convey.ShouldEqual, tt.expectedPath)
if tt.expectedErr == nil {
convey.So(gotErr, convey.ShouldBeNil)
} else {
convey.So(gotErr, convey.ShouldNotBeNil)
}
})
}
})
}
func TestGetMaxVirtualIDByPhysicalID(t *testing.T) {
convey.Convey("Test GetMaxVirtualIDByPhysicalID", t, func() {
convey.Convey("When getNPUInfoConfigBaseDir returns error", func() {
patches := gomonkey.ApplyFuncReturn(getNPUInfoConfigBaseDir, "", errors.New("dir not exist"))
defer patches.Reset()
maxVID, err := GetMaxVirtualIDByPhysicalID(0)
convey.So(err, convey.ShouldNotBeNil)
convey.So(maxVID, convey.ShouldEqual, MinVirtualID)
})
convey.Convey("When collectMatchedVirtualIDs returns error", func() {
patches := gomonkey.ApplyFuncReturn(getNPUInfoConfigBaseDir, SoftShareDevNPUInfoConfigParentDirPath, nil)
patches.ApplyFuncReturn(collectMatchedVirtualIDs, nil, errors.New("read dir failed"))
defer patches.Reset()
maxVID, err := GetMaxVirtualIDByPhysicalID(0)
convey.So(err, convey.ShouldNotBeNil)
convey.So(maxVID, convey.ShouldEqual, MinVirtualID)
})
convey.Convey("When all steps success, return max VirtualID", func() {
patches := gomonkey.ApplyFuncReturn(getNPUInfoConfigBaseDir, SoftShareDevNPUInfoConfigParentDirPath, nil)
patches.ApplyFuncReturn(collectMatchedVirtualIDs, []int{1, num5, num3}, nil)
patches.ApplyFuncReturn(calculateMaxVirtualID, num5)
defer patches.Reset()
maxVID, err := GetMaxVirtualIDByPhysicalID(0)
convey.So(err, convey.ShouldBeNil)
convey.So(maxVID, convey.ShouldEqual, num5)
})
convey.Convey("When no matched VirtualID, return -1", func() {
patches := gomonkey.ApplyFuncReturn(getNPUInfoConfigBaseDir, SoftShareDevNPUInfoConfigParentDirPath, nil)
patches.ApplyFuncReturn(collectMatchedVirtualIDs, []int{}, nil)
defer patches.Reset()
maxVID, err := GetMaxVirtualIDByPhysicalID(0)
convey.So(err, convey.ShouldBeNil)
convey.So(maxVID, convey.ShouldEqual, -1)
})
})
}
func TestGetNPUInfoConfigBaseDir(t *testing.T) {
convey.Convey("Test getNPUInfoConfigBaseDir", t, func() {
convey.Convey("When RealDirChecker returns error", func() {
patches := gomonkey.ApplyFuncReturn(utils.RealDirChecker, "", errors.New("dir not exist"))
defer patches.Reset()
dirPath, err := getNPUInfoConfigBaseDir()
convey.So(err, convey.ShouldNotBeNil)
convey.So(dirPath, convey.ShouldBeEmpty)
convey.So(err.Error(), convey.ShouldContainSubstring, "stat npu info config dir")
})
convey.Convey("When RealDirChecker success", func() {
patches := gomonkey.ApplyFuncReturn(utils.RealDirChecker, SoftShareDevNPUInfoConfigParentDirPath, nil)
defer patches.Reset()
dirPath, err := getNPUInfoConfigBaseDir()
convey.So(err, convey.ShouldBeNil)
convey.So(dirPath, convey.ShouldEqual, SoftShareDevNPUInfoConfigParentDirPath)
})
})
}
func TestCollectMatchedVirtualIDs(t *testing.T) {
convey.Convey("Test collectMatchedVirtualIDs", t, func() {
convey.Convey("When os.ReadDir base dir returns error", func() {
patches := gomonkey.ApplyFuncReturn(os.ReadDir, nil, errors.New("permission denied"))
defer patches.Reset()
vidList, err := collectMatchedVirtualIDs(SoftShareDevNPUInfoConfigParentDirPath, 0)
convey.So(err, convey.ShouldNotBeNil)
convey.So(vidList, convey.ShouldBeNil)
convey.So(err.Error(), convey.ShouldContainSubstring, "read base dir")
})
convey.Convey("When os.ReadDir sub dir returns error", func() {
patches := gomonkey.ApplyFuncReturn(os.ReadDir, nil, errors.New("dir not exist"))
defer patches.Reset()
vidList, err := collectMatchedVirtualIDs(SoftShareDevNPUInfoConfigParentDirPath, 0)
convey.So(err, convey.ShouldNotBeNil)
convey.So(vidList, convey.ShouldBeEmpty)
})
})
}
func TestParseVirtualIDFromDirName(t *testing.T) {
convey.Convey("Test parseVirtualIDFromDirName", t, func() {
physicalIDStr := strconv.Itoa(0)
convey.Convey("When dir name format is invalid", func() {
vid, err := parseVirtualIDFromDirName("100", physicalIDStr)
convey.So(err, convey.ShouldNotBeNil)
convey.So(vid, convey.ShouldEqual, -1)
convey.So(err.Error(), convey.ShouldContainSubstring, "invalid dir name format")
})
convey.Convey("When PhysicalID not match", func() {
vid, err := parseVirtualIDFromDirName("200_5", physicalIDStr)
convey.So(err, convey.ShouldBeNil)
convey.So(vid, convey.ShouldEqual, -1)
})
convey.Convey("When parse success", func() {
vid, err := parseVirtualIDFromDirName("0_1", physicalIDStr)
convey.So(err, convey.ShouldBeNil)
convey.So(vid, convey.ShouldEqual, 1)
})
})
}
func TestCalculateMaxVirtualID(t *testing.T) {
convey.Convey("Test calculateMaxVirtualID", t, func() {
convey.Convey("When virtualIDs is empty", func() {
maxVID := calculateMaxVirtualID([]int{})
convey.So(maxVID, convey.ShouldEqual, -1)
})
convey.Convey("When virtualIDs has multiple elements", func() {
maxVID := calculateMaxVirtualID([]int{1, num5, num3})
convey.So(maxVID, convey.ShouldEqual, num5)
})
})
}
func TestSliceEqual(t *testing.T) {
convey.Convey("Test SliceEqual", t, func() {
convey.Convey("When two slices are equal, should return true", func() {
convey.So(SliceEqual[int]([]int{1, 2, 3}, []int{1, 2, 3}), convey.ShouldBeTrue)
})
convey.Convey("When two slices are both empty, should return true", func() {
convey.So(SliceEqual[int]([]int{}, []int{}), convey.ShouldBeTrue)
})
convey.Convey("When lengths differ, should return false", func() {
convey.So(SliceEqual[int]([]int{1, 2}, []int{1, 2, 3}), convey.ShouldBeFalse)
})
convey.Convey("When same length but different elements, should return false", func() {
convey.So(SliceEqual[int]([]int{1, 2, 3}, []int{1, 2, 4}), convey.ShouldBeFalse)
})
})
}