From 9ea6364f7a0d81c198d4103e7ffd2fd6452a5979 Mon Sep 17 00:00:00 2001 From: Jon Huhn Date: Tue, 10 Dec 2024 13:50:19 -0600 Subject: [PATCH] add unit tests --- .github/workflows/tests.yaml | 5 +- .../resource/gpu/v1alpha1/api_test.go | 116 ++++++++++++++ .../resource/gpu/v1alpha1/sharing_test.go | 147 ++++++++++++++++++ .../resource/gpu/v1alpha1/validate_test.go | 110 +++++++++++++ cmd/dra-example-kubeletplugin/state_test.go | 60 +++++++ go.mod | 2 +- 6 files changed, 436 insertions(+), 4 deletions(-) create mode 100644 api/example.com/resource/gpu/v1alpha1/api_test.go create mode 100644 api/example.com/resource/gpu/v1alpha1/sharing_test.go create mode 100644 api/example.com/resource/gpu/v1alpha1/validate_test.go create mode 100644 cmd/dra-example-kubeletplugin/state_test.go diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 1ab65bbe..1a9f46bd 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -25,6 +25,5 @@ jobs: run: make PREFIX=artifacts cmds - name: List binaries run: ls -al artifacts/ - # no tests yet - # - name: Test - # run: go test -v -race ./... + - name: Test + run: go test -v -race ./... diff --git a/api/example.com/resource/gpu/v1alpha1/api_test.go b/api/example.com/resource/gpu/v1alpha1/api_test.go new file mode 100644 index 00000000..d761faa3 --- /dev/null +++ b/api/example.com/resource/gpu/v1alpha1/api_test.go @@ -0,0 +1,116 @@ +/* + * Copyright 2024 The Kubernetes 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 v1alpha1 + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestGpuConfigNormalize(t *testing.T) { + tests := []struct { + name string + gpuConfig *GpuConfig + expected *GpuConfig + expectedErr string + }{ + { + name: "nil GpuConfig", + gpuConfig: nil, + expectedErr: "config is 'nil'", + }, + { + name: "empty GpuConfig", + gpuConfig: &GpuConfig{}, + expected: &GpuConfig{ + Sharing: &GpuSharing{ + Strategy: TimeSlicingStrategy, + TimeSlicingConfig: &TimeSlicingConfig{ + Interval: DefaultTimeSlice, + }, + }, + }, + }, + { + name: "empty GpuConfig with SpacePartitioning", + gpuConfig: &GpuConfig{ + Sharing: &GpuSharing{ + Strategy: SpacePartitioningStrategy, + }, + }, + expected: &GpuConfig{ + Sharing: &GpuSharing{ + Strategy: SpacePartitioningStrategy, + SpacePartitioningConfig: &SpacePartitioningConfig{ + PartitionCount: 1, + }, + }, + }, + }, + { + name: "full GpuConfig", + gpuConfig: &GpuConfig{ + Sharing: &GpuSharing{ + Strategy: SpacePartitioningStrategy, + TimeSlicingConfig: &TimeSlicingConfig{ + Interval: ShortTimeSlice, + }, + SpacePartitioningConfig: &SpacePartitioningConfig{ + PartitionCount: 5, + }, + }, + }, + expected: &GpuConfig{ + Sharing: &GpuSharing{ + Strategy: SpacePartitioningStrategy, + TimeSlicingConfig: &TimeSlicingConfig{ + Interval: ShortTimeSlice, + }, + SpacePartitioningConfig: &SpacePartitioningConfig{ + PartitionCount: 5, + }, + }, + }, + }, + { + name: "default GpuConfig", + gpuConfig: DefaultGpuConfig(), + expected: DefaultGpuConfig(), // default should be fully normalized already + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := test.gpuConfig.Normalize() + if test.expectedErr != "" { + if err == nil { + t.Errorf("expected error %q, but got no error", test.expectedErr) + } else if err.Error() != test.expectedErr { + t.Errorf("expected error %q, but got %v", test.expectedErr, err) + } + } else { + if err != nil { + t.Errorf("expected Normalize to succeed, got error %v", err) + } + } + if diff := cmp.Diff(test.gpuConfig, test.expected); diff != "" { + t.Error("expected configs to be equal:\n", diff) + } + }) + } +} diff --git a/api/example.com/resource/gpu/v1alpha1/sharing_test.go b/api/example.com/resource/gpu/v1alpha1/sharing_test.go new file mode 100644 index 00000000..b97966ef --- /dev/null +++ b/api/example.com/resource/gpu/v1alpha1/sharing_test.go @@ -0,0 +1,147 @@ +/* + * Copyright 2024 The Kubernetes 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 v1alpha1 + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestGpuSharingGetTimeSlicingConfig(t *testing.T) { + tests := []struct { + name string + gpuSharing *GpuSharing + expected *TimeSlicingConfig + expectedErr string + }{ + { + name: "nil GpuSharing", + gpuSharing: nil, + expectedErr: "no sharing set to get config from", + }, + { + name: "strategy is not TimeSlicing", + gpuSharing: &GpuSharing{ + Strategy: "not" + TimeSlicingStrategy, + }, + expectedErr: "strategy is not set to 'TimeSlicing'", + }, + { + name: "non-nil SpacePartitioningConfig", + gpuSharing: &GpuSharing{ + Strategy: TimeSlicingStrategy, + SpacePartitioningConfig: &SpacePartitioningConfig{}, + }, + expectedErr: "cannot use SpacePartitioningConfig with the 'TimeSlicing' strategy", + }, + { + name: "valid TimeSlicingConfig", + gpuSharing: &GpuSharing{ + Strategy: TimeSlicingStrategy, + TimeSlicingConfig: &TimeSlicingConfig{ + Interval: LongTimeSlice, + }, + }, + expected: &TimeSlicingConfig{ + Interval: LongTimeSlice, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + timeSlicing, err := test.gpuSharing.GetTimeSlicingConfig() + if test.expectedErr != "" { + if err == nil { + t.Errorf("expected error %q, but got no error", test.expectedErr) + } else if err.Error() != test.expectedErr { + t.Errorf("expected error %q, but got %v", test.expectedErr, err) + } + } else { + if err != nil { + t.Errorf("expected GetTimeSlicingConfig to succeed, got error %v", err) + } + } + if diff := cmp.Diff(timeSlicing, test.expected); diff != "" { + t.Error("expected time slicing configs to be equal:\n", diff) + } + }) + } + +} +func TestGpuSharingGetSpacePartitioningConfig(t *testing.T) { + tests := []struct { + name string + gpuSharing *GpuSharing + expected *SpacePartitioningConfig + expectedErr string + }{ + { + name: "nil GpuSharing", + gpuSharing: nil, + expectedErr: "no sharing set to get config from", + }, + { + name: "strategy is not SpacePartitioning", + gpuSharing: &GpuSharing{ + Strategy: "not" + SpacePartitioningStrategy, + }, + expectedErr: "strategy is not set to 'SpacePartitioning'", + }, + { + name: "non-nil TimeSlicingConfig", + gpuSharing: &GpuSharing{ + Strategy: SpacePartitioningStrategy, + TimeSlicingConfig: &TimeSlicingConfig{}, + }, + expectedErr: "cannot use TimeSlicingConfig with the 'SpacePartitioning' strategy", + }, + { + name: "valid SpacePartitioningConfig", + gpuSharing: &GpuSharing{ + Strategy: SpacePartitioningStrategy, + SpacePartitioningConfig: &SpacePartitioningConfig{ + PartitionCount: 5, + }, + }, + expected: &SpacePartitioningConfig{ + PartitionCount: 5, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + spacePartitioning, err := test.gpuSharing.GetSpacePartitioningConfig() + if test.expectedErr != "" { + if err == nil { + t.Errorf("expected error %q, but got no error", test.expectedErr) + } else if err.Error() != test.expectedErr { + t.Errorf("expected error %q, but got %v", test.expectedErr, err) + } + } else { + if err != nil { + t.Errorf("expected GetSpacePartitioningConfig to succeed, got error %v", err) + } + } + if diff := cmp.Diff(spacePartitioning, test.expected); diff != "" { + t.Error("expected space partitioning configs to be equal:\n", diff) + } + }) + } +} diff --git a/api/example.com/resource/gpu/v1alpha1/validate_test.go b/api/example.com/resource/gpu/v1alpha1/validate_test.go new file mode 100644 index 00000000..4080a624 --- /dev/null +++ b/api/example.com/resource/gpu/v1alpha1/validate_test.go @@ -0,0 +1,110 @@ +/* + * Copyright 2024 The Kubernetes 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 v1alpha1 + +import ( + "testing" +) + +func TestGpuConfigValidate(t *testing.T) { + tests := []struct { + name string + gpuConfig *GpuConfig + expected string + }{ + { + name: "empty GpuConfig", + gpuConfig: &GpuConfig{}, + expected: "no sharing strategy set", + }, + { + name: "empty GpuConfig.Sharing", + gpuConfig: &GpuConfig{ + Sharing: &GpuSharing{}, + }, + expected: "unknown GPU sharing strategy: ", + }, + { + name: "empty GpuConfig.Sharing.TimeSlicingConfig", + gpuConfig: &GpuConfig{ + Sharing: &GpuSharing{ + Strategy: TimeSlicingStrategy, + TimeSlicingConfig: &TimeSlicingConfig{}, + }, + }, + expected: "unknown time-slice interval: ", + }, + { + name: "valid GpuConfig with TimeSlicing", + gpuConfig: &GpuConfig{ + Sharing: &GpuSharing{ + Strategy: TimeSlicingStrategy, + TimeSlicingConfig: &TimeSlicingConfig{ + Interval: MediumTimeSlice, + }, + }, + }, + expected: "", + }, + { + name: "negative GpuConfig.Sharing.SpacePartitioningConfig.PartitionCount", + gpuConfig: &GpuConfig{ + Sharing: &GpuSharing{ + Strategy: SpacePartitioningStrategy, + SpacePartitioningConfig: &SpacePartitioningConfig{ + PartitionCount: -1, + }, + }, + }, + expected: "invalid partition count: -1", + }, + { + name: "valid GpuConfig with SpacePartitioning", + gpuConfig: &GpuConfig{ + Sharing: &GpuSharing{ + Strategy: SpacePartitioningStrategy, + SpacePartitioningConfig: &SpacePartitioningConfig{ + PartitionCount: 1000, + }, + }, + }, + expected: "", + }, + { + name: "default GpuConfig", + gpuConfig: DefaultGpuConfig(), + expected: "", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := test.gpuConfig.Validate() + if test.expected != "" { + if err == nil { + t.Errorf("expected error %q, but got no error", test.expected) + } else if err.Error() != test.expected { + t.Errorf("expected error %q, but got %v", test.expected, err) + } + } else { + if err != nil { + t.Errorf("expected Validate to succeed, got error %v", err) + } + } + }) + } +} diff --git a/cmd/dra-example-kubeletplugin/state_test.go b/cmd/dra-example-kubeletplugin/state_test.go new file mode 100644 index 00000000..62445faf --- /dev/null +++ b/cmd/dra-example-kubeletplugin/state_test.go @@ -0,0 +1,60 @@ +/* + * Copyright 2024 The Kubernetes 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 main + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + drapbv1 "k8s.io/kubelet/pkg/apis/dra/v1alpha4" +) + +func TestPreparedDevicesGetDevices(t *testing.T) { + tests := []struct { + name string + preparedDevices PreparedDevices + expected []*drapbv1.Device + }{ + { + name: "nil PreparedDevices", + preparedDevices: nil, + expected: nil, + }, + { + name: "several PreparedDevices", + preparedDevices: PreparedDevices{ + {Device: drapbv1.Device{DeviceName: "dev1"}}, + {Device: drapbv1.Device{DeviceName: "dev2"}}, + {Device: drapbv1.Device{DeviceName: "dev3"}}, + }, + expected: []*drapbv1.Device{ + {DeviceName: "dev1"}, + {DeviceName: "dev2"}, + {DeviceName: "dev3"}, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + devices := test.preparedDevices.GetDevices() + if diff := cmp.Diff(devices, test.expected); diff != "" { + t.Error("expected devices to be equal:\n", diff) + } + }) + } +} diff --git a/go.mod b/go.mod index da7d7915..dde125e8 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module sigs.k8s.io/dra-example-driver go 1.23.1 require ( + github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/spf13/pflag v1.0.5 github.com/urfave/cli/v2 v2.25.3 @@ -36,7 +37,6 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/imdario/mergo v0.3.6 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect