Skip to content

Commit 00b1f90

Browse files
committed
Add unit tests for device management and configuration in builder
Signed-off-by: Harsh Rawat <harshrawat@microsoft.com>
1 parent 4ea99ee commit 00b1f90

10 files changed

Lines changed: 441 additions & 0 deletions

File tree

internal/vm/builder/boot_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//go:build windows
2+
3+
package builder
4+
5+
import (
6+
"testing"
7+
8+
"github.com/Microsoft/hcsshim/internal/vm"
9+
)
10+
11+
func TestBootConfig(t *testing.T) {
12+
b, cs := newBuilder(t, vm.Linux)
13+
b.Boot().SetUEFIBoot("root", "path", "args")
14+
15+
if cs.VirtualMachine.Chipset.Uefi == nil || cs.VirtualMachine.Chipset.Uefi.BootThis == nil {
16+
t.Fatal("UEFI boot not applied")
17+
}
18+
19+
boot := cs.VirtualMachine.Chipset.Uefi.BootThis
20+
if boot.DevicePath != "path" {
21+
t.Fatalf("UEFI DevicePath = %q, want %q", boot.DevicePath, "path")
22+
}
23+
if boot.DeviceType != "VmbFs" {
24+
t.Fatalf("UEFI DeviceType = %q, want %q", boot.DeviceType, "VmbFs")
25+
}
26+
if boot.VmbFsRootPath != "root" {
27+
t.Fatalf("UEFI VmbFsRootPath = %q, want %q", boot.VmbFsRootPath, "root")
28+
}
29+
if boot.OptionalData != "args" {
30+
t.Fatalf("UEFI OptionalData = %q, want %q", boot.OptionalData, "args")
31+
}
32+
33+
err := b.Boot().SetLinuxKernelDirectBoot("kernel", "initrd", "cmd")
34+
if err != nil {
35+
t.Fatalf("SetLinuxKernelDirectBoot error = %v", err)
36+
}
37+
if cs.VirtualMachine.Chipset.LinuxKernelDirect == nil {
38+
t.Fatal("LinuxKernelDirect not applied")
39+
}
40+
lkd := cs.VirtualMachine.Chipset.LinuxKernelDirect
41+
if lkd.KernelFilePath != "kernel" {
42+
t.Fatalf("LinuxKernelDirect KernelFilePath = %q, want %q", lkd.KernelFilePath, "kernel")
43+
}
44+
if lkd.InitRdPath != "initrd" {
45+
t.Fatalf("LinuxKernelDirect InitRdPath = %q, want %q", lkd.InitRdPath, "initrd")
46+
}
47+
if lkd.KernelCmdLine != "cmd" {
48+
t.Fatalf("LinuxKernelDirect KernelCmdLine = %q, want %q", lkd.KernelCmdLine, "cmd")
49+
}
50+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//go:build windows
2+
3+
package builder
4+
5+
import (
6+
"testing"
7+
8+
hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
9+
"github.com/Microsoft/hcsshim/internal/vm"
10+
"github.com/pkg/errors"
11+
)
12+
13+
func newBuilder(t *testing.T, guestOS vm.GuestOS) (Builder, *hcsschema.ComputeSystem) {
14+
t.Helper()
15+
b, err := New("owner", guestOS)
16+
if err != nil {
17+
t.Fatalf("New() error = %v", err)
18+
}
19+
cs, ok := b.Get().(*hcsschema.ComputeSystem)
20+
if !ok {
21+
t.Fatalf("builder.Get() type = %T, want *hcsschema.ComputeSystem", b.Get())
22+
}
23+
return b, cs
24+
}
25+
26+
func TestNewBuilder_InvalidGuestOS(t *testing.T) {
27+
if _, err := New("owner", vm.GuestOS("unknown")); !errors.Is(err, errUnknownGuestOS) {
28+
t.Fatalf("New() error = %v, want %v", err, errUnknownGuestOS)
29+
}
30+
}
31+
32+
func TestNewBuilder_DefaultDevices(t *testing.T) {
33+
_, cs := newBuilder(t, vm.Windows)
34+
if cs.VirtualMachine.Devices.VirtualSmb == nil {
35+
t.Fatal("VirtualSmb should be initialized for Windows")
36+
}
37+
if cs.VirtualMachine.Devices.Plan9 != nil {
38+
t.Fatal("Plan9 should be nil for Windows")
39+
}
40+
41+
_, cs = newBuilder(t, vm.Linux)
42+
if cs.VirtualMachine.Devices.Plan9 == nil {
43+
t.Fatal("Plan9 should be initialized for Linux")
44+
}
45+
if cs.VirtualMachine.Devices.VirtualSmb != nil {
46+
t.Fatal("VirtualSmb should be nil for Linux")
47+
}
48+
}

internal/vm/builder/device_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//go:build windows
2+
3+
package builder
4+
5+
import (
6+
"strconv"
7+
"testing"
8+
9+
"github.com/Microsoft/hcsshim/internal/vm"
10+
"github.com/pkg/errors"
11+
)
12+
13+
func TestVPCIDevice(t *testing.T) {
14+
b, cs := newBuilder(t, vm.Linux)
15+
device := VPCIDeviceID{DeviceInstanceID: "PCI\\VEN_1234", VirtualFunctionIndex: 2}
16+
17+
if err := b.Device().AddVPCIDevice(device, true); err != nil {
18+
t.Fatalf("AddVPCIDevice error = %v", err)
19+
}
20+
if len(cs.VirtualMachine.Devices.VirtualPci) != 1 {
21+
t.Fatalf("VirtualPci entries = %d, want 1", len(cs.VirtualMachine.Devices.VirtualPci))
22+
}
23+
for _, entry := range cs.VirtualMachine.Devices.VirtualPci {
24+
if len(entry.Functions) != 1 {
25+
t.Fatalf("VirtualPci Functions = %d, want 1", len(entry.Functions))
26+
}
27+
if entry.Functions[0].DeviceInstancePath != device.DeviceInstanceID || entry.Functions[0].VirtualFunction != device.VirtualFunctionIndex {
28+
t.Fatal("VPCI function not applied as expected")
29+
}
30+
if entry.PropagateNumaAffinity == nil || !*entry.PropagateNumaAffinity {
31+
t.Fatal("PropagateNumaAffinity should be true")
32+
}
33+
}
34+
35+
if err := b.Device().AddVPCIDevice(device, false); !errors.Is(err, errAlreadySet) {
36+
t.Fatalf("AddVPCIDevice duplicate error = %v, want %v", err, errAlreadySet)
37+
}
38+
}
39+
40+
func TestSerialConsoleAndGraphics(t *testing.T) {
41+
b, cs := newBuilder(t, vm.Linux)
42+
if err := b.Device().SetSerialConsole(1, "not-a-pipe"); err == nil {
43+
t.Fatal("SetSerialConsole should reject non-pipe path")
44+
}
45+
46+
pipePath := `\\.\pipe\serial`
47+
if err := b.Device().SetSerialConsole(1, pipePath); err != nil {
48+
t.Fatalf("SetSerialConsole error = %v", err)
49+
}
50+
key := strconv.Itoa(1)
51+
if cs.VirtualMachine.Devices.ComPorts[key].NamedPipe != pipePath {
52+
t.Fatal("serial console named pipe not set as expected")
53+
}
54+
55+
b.Device().EnableGraphicsConsole()
56+
if cs.VirtualMachine.Devices.Keyboard == nil || cs.VirtualMachine.Devices.EnhancedModeVideo == nil || cs.VirtualMachine.Devices.VideoMonitor == nil {
57+
t.Fatal("graphics console devices not enabled")
58+
}
59+
}

internal/vm/builder/memory_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//go:build windows
2+
3+
package builder
4+
5+
import (
6+
"testing"
7+
8+
hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
9+
"github.com/Microsoft/hcsshim/internal/vm"
10+
)
11+
12+
func TestMemoryConfig(t *testing.T) {
13+
b, cs := newBuilder(t, vm.Linux)
14+
b.Memory().SetMemoryLimit(512)
15+
if cs.VirtualMachine.ComputeTopology.Memory.SizeInMB != 512 {
16+
t.Fatalf("SizeInMB = %d, want %d", cs.VirtualMachine.ComputeTopology.Memory.SizeInMB, 512)
17+
}
18+
19+
b.Memory().SetMemoryConfig(&MemoryConfig{
20+
BackingType: MemoryBackingTypeVirtual,
21+
DeferredCommit: true,
22+
HotHint: true,
23+
ColdHint: false,
24+
ColdDiscardHint: true,
25+
})
26+
mem := cs.VirtualMachine.ComputeTopology.Memory
27+
if !mem.AllowOvercommit || !mem.EnableDeferredCommit || !mem.EnableHotHint || mem.EnableColdHint || !mem.EnableColdDiscardHint {
28+
t.Fatal("memory config not applied as expected")
29+
}
30+
31+
b.Memory().SetMemoryConfig(&MemoryConfig{
32+
BackingType: MemoryBackingTypePhysical,
33+
DeferredCommit: true,
34+
HotHint: true,
35+
ColdHint: false,
36+
ColdDiscardHint: true,
37+
})
38+
mem = cs.VirtualMachine.ComputeTopology.Memory
39+
if mem.AllowOvercommit || !mem.EnableDeferredCommit || !mem.EnableHotHint || mem.EnableColdHint || !mem.EnableColdDiscardHint {
40+
t.Fatal("memory config not applied as expected")
41+
}
42+
43+
b.Memory().SetMMIOConfig(64, 128, 256)
44+
if mem.LowMMIOGapInMB != 64 || mem.HighMMIOBaseInMB != 128 || mem.HighMMIOGapInMB != 256 {
45+
t.Fatal("MMIO config not applied as expected")
46+
}
47+
48+
b.Memory().SetFirmwareFallbackMeasuredSlit()
49+
if mem.SlitType == nil || *mem.SlitType != hcsschema.VirtualSlitType_FIRMWARE_FALLBACK_MEASURED {
50+
t.Fatal("SlitType not set to FIRMWARE_FALLBACK_MEASURED")
51+
}
52+
}

internal/vm/builder/numa_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//go:build windows
2+
3+
package builder
4+
5+
import (
6+
"testing"
7+
8+
hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
9+
"github.com/Microsoft/hcsshim/internal/vm"
10+
)
11+
12+
func TestNUMASettings(t *testing.T) {
13+
b, cs := newBuilder(t, vm.Linux)
14+
virtualNodeCount := uint8(2)
15+
maxSizePerNode := uint64(4096)
16+
numaProcessors := &hcsschema.NumaProcessors{
17+
CountPerNode: hcsschema.Range{Max: 4},
18+
NodePerSocket: 1,
19+
}
20+
numa := &hcsschema.Numa{
21+
VirtualNodeCount: virtualNodeCount,
22+
PreferredPhysicalNodes: []int64{0, 1},
23+
Settings: []hcsschema.NumaSetting{
24+
{
25+
VirtualNodeNumber: 0,
26+
PhysicalNodeNumber: 0,
27+
VirtualSocketNumber: 0,
28+
CountOfProcessors: 2,
29+
CountOfMemoryBlocks: 1024,
30+
MemoryBackingType: hcsschema.MemoryBackingType_VIRTUAL,
31+
},
32+
{
33+
VirtualNodeNumber: 1,
34+
PhysicalNodeNumber: 1,
35+
VirtualSocketNumber: 1,
36+
CountOfProcessors: 2,
37+
CountOfMemoryBlocks: 1024,
38+
MemoryBackingType: hcsschema.MemoryBackingType_PHYSICAL,
39+
},
40+
},
41+
MaxSizePerNode: maxSizePerNode,
42+
}
43+
44+
b.Numa().SetNUMAProcessorsSettings(numaProcessors)
45+
b.Numa().SetNUMASettings(numa)
46+
47+
gotProcessors := cs.VirtualMachine.ComputeTopology.Processor.NumaProcessorsSettings
48+
if gotProcessors == nil {
49+
t.Fatal("NUMA processor settings not applied")
50+
}
51+
if gotProcessors.CountPerNode.Max != 4 {
52+
t.Fatalf("CountPerNode.Max = %d, want %d", gotProcessors.CountPerNode.Max, 4)
53+
}
54+
if gotProcessors.NodePerSocket != 1 {
55+
t.Fatalf("NodePerSocket = %d, want %d", gotProcessors.NodePerSocket, 1)
56+
}
57+
58+
gotNUMA := cs.VirtualMachine.ComputeTopology.Numa
59+
if gotNUMA == nil {
60+
t.Fatal("NUMA settings not applied")
61+
}
62+
if gotNUMA.VirtualNodeCount != virtualNodeCount {
63+
t.Fatalf("VirtualNodeCount = %d, want %d", gotNUMA.VirtualNodeCount, virtualNodeCount)
64+
}
65+
if gotNUMA.MaxSizePerNode != maxSizePerNode {
66+
t.Fatalf("MaxSizePerNode = %d, want %d", gotNUMA.MaxSizePerNode, maxSizePerNode)
67+
}
68+
if len(gotNUMA.PreferredPhysicalNodes) != 2 || gotNUMA.PreferredPhysicalNodes[0] != 0 || gotNUMA.PreferredPhysicalNodes[1] != 1 {
69+
t.Fatalf("PreferredPhysicalNodes = %v, want [0 1]", gotNUMA.PreferredPhysicalNodes)
70+
}
71+
if len(gotNUMA.Settings) != 2 {
72+
t.Fatalf("Settings length = %d, want %d", len(gotNUMA.Settings), 2)
73+
}
74+
75+
first := gotNUMA.Settings[0]
76+
if first.VirtualNodeNumber != 0 || first.PhysicalNodeNumber != 0 || first.VirtualSocketNumber != 0 {
77+
t.Fatal("first NUMA setting node/socket numbers not applied as expected")
78+
}
79+
if first.CountOfProcessors != 2 || first.CountOfMemoryBlocks != 1024 {
80+
t.Fatal("first NUMA setting processor/memory counts not applied as expected")
81+
}
82+
if first.MemoryBackingType != hcsschema.MemoryBackingType_VIRTUAL {
83+
t.Fatalf("first NUMA setting MemoryBackingType = %s, want %s", first.MemoryBackingType, hcsschema.MemoryBackingType_VIRTUAL)
84+
}
85+
86+
second := gotNUMA.Settings[1]
87+
if second.VirtualNodeNumber != 1 || second.PhysicalNodeNumber != 1 || second.VirtualSocketNumber != 1 {
88+
t.Fatal("second NUMA setting node/socket numbers not applied as expected")
89+
}
90+
if second.CountOfProcessors != 2 || second.CountOfMemoryBlocks != 1024 {
91+
t.Fatal("second NUMA setting processor/memory counts not applied as expected")
92+
}
93+
if second.MemoryBackingType != hcsschema.MemoryBackingType_PHYSICAL {
94+
t.Fatalf("second NUMA setting MemoryBackingType = %s, want %s", second.MemoryBackingType, hcsschema.MemoryBackingType_PHYSICAL)
95+
}
96+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//go:build windows
2+
3+
package builder
4+
5+
import (
6+
"testing"
7+
8+
"github.com/Microsoft/hcsshim/internal/vm"
9+
)
10+
11+
func TestProcessorConfigAndCPUGroup(t *testing.T) {
12+
b, cs := newBuilder(t, vm.Linux)
13+
b.Processor().SetProcessorConfig(&ProcessorConfig{Count: 4, Limit: 2500, Weight: 200})
14+
proc := cs.VirtualMachine.ComputeTopology.Processor
15+
if proc.Count != 4 || proc.Limit != 2500 || proc.Weight != 200 {
16+
t.Fatal("processor config not applied as expected")
17+
}
18+
19+
b.Processor().SetCPUGroup("cg1")
20+
if proc.CpuGroup == nil || proc.CpuGroup.Id != "cg1" {
21+
t.Fatal("cpu group not applied as expected")
22+
}
23+
}

internal/vm/builder/scsi_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//go:build windows
2+
3+
package builder
4+
5+
import (
6+
"testing"
7+
8+
"github.com/Microsoft/hcsshim/internal/vm"
9+
)
10+
11+
func TestSCSI(t *testing.T) {
12+
b, cs := newBuilder(t, vm.Linux)
13+
14+
if err := b.Device().AddSCSIDisk("0", "1", "disk.vhdx", vm.SCSIDiskTypeVirtualDisk, false); err == nil {
15+
t.Fatal("AddSCSIDisk should fail when controller missing")
16+
}
17+
18+
b.Device().AddSCSIController("0")
19+
if err := b.Device().AddSCSIDisk("0", "1", "disk.vhdx", vm.SCSIDiskTypeVirtualDisk, true); err != nil {
20+
t.Fatalf("AddSCSIDisk error = %v", err)
21+
}
22+
23+
ctrl := cs.VirtualMachine.Devices.Scsi["0"]
24+
att := ctrl.Attachments["1"]
25+
if att.Path != "disk.vhdx" || att.Type_ != vm.SCSIDiskTypeVirtualDisk.String() || !att.ReadOnly {
26+
t.Fatal("SCSI attachment not applied as expected")
27+
}
28+
29+
if err := b.Device().AddSCSIDisk("0", "2", "disk.vhdx", vm.SCSIDiskType(99), false); err == nil {
30+
t.Fatal("AddSCSIDisk should reject unsupported disk type")
31+
}
32+
if err := b.Device().AddSCSIDisk("missing", "1", "disk.vhdx", vm.SCSIDiskTypeVirtualDisk, false); err == nil {
33+
t.Fatal("AddSCSIDisk should fail when controller does not exist")
34+
}
35+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//go:build windows
2+
3+
package builder
4+
5+
import (
6+
"testing"
7+
8+
"github.com/Microsoft/hcsshim/internal/vm"
9+
)
10+
11+
func TestStorageQoS(t *testing.T) {
12+
b, cs := newBuilder(t, vm.Linux)
13+
b.StorageQoS().SetStorageQoS(1000, 2000)
14+
if cs.VirtualMachine.StorageQoS == nil {
15+
t.Fatal("StorageQoS should be initialized")
16+
}
17+
if cs.VirtualMachine.StorageQoS.IopsMaximum != 1000 || cs.VirtualMachine.StorageQoS.BandwidthMaximum != 2000 {
18+
t.Fatal("StorageQoS not applied as expected")
19+
}
20+
}

0 commit comments

Comments
 (0)