Skip to content

Commit 35ea6c5

Browse files
author
Shreyansh Sancheti
committed
controller/vm: add unit tests for VM Controller state machine
Defines per-platform vmLifetime and guestManager seam interfaces over *vmmanager.UtilityVM and *guestmanager.Guest. Each interface is a superset of the methods Controller invokes directly plus the device methods consumed by the wired-in sub-controllers (vpci/network/plan9/scsi). *vmmanager and *guestmanager satisfy these via Go structural typing; no concrete sister fields needed. The per-platform split is necessary because: - AddPlan9 / RemovePlan9 are LCOW-only on *vmmanager.UtilityVM. - Guest network signatures differ across LCOW and WCOW. - vPCI guest and Plan9 guest methods are LCOW-only. - UpdateHvSocketAddress is WCOW-only. Guest() uses a type assertion to preserve its concrete return type. 30 tests covering: Idempotency (3): duplicate StartVM no-op, TerminateVM from Terminated and NotCreated. State guards (8): StartVM rejects NotCreated/Terminated/Invalid, ExecIntoHost rejects Terminated, terminal+stderr input validation, Update methods reject non-Running (4 subtests), Stats rejects Terminated, Wait rejects NotCreated, DumpStacks rejects Terminated, UpdateCPUGroup rejects empty ID. TerminateVM cleanup chain (6): full Terminate->CloseConnection->Close ordering, guest close failure swallowed, uvm.Terminate failure swallowed, uvm.Close failure -> StateInvalid, recovery from Invalid, retry failure stays Invalid. Error propagation (4): ExecIntoHost guest error, DumpStacks guest error, UpdatePolicyFragment guest error, ExitStatus not-terminated error. API delegation (4): ExecIntoHost exit code forwarding, DumpStacks with and without capability, ExitStatus value assembly. Wait + background goroutine (5): Wait from Terminated with log drain, Wait context cancelled during log drain, Wait Running happy path, waitForVMExit Running->Terminated transition, waitForVMExit overwrites Invalid->Terminated (pins current behaviour). Mocks generated per-platform (mocks/mock_lcow_vm.go, mock_wcow_vm.go) with matching //go:build constraints. Signed-off-by: Shreyansh Sancheti <shsancheti@microsoft.com>
1 parent a4051da commit 35ea6c5

8 files changed

Lines changed: 2139 additions & 4 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//go:build windows && (lcow || wcow)
2+
3+
package vm
4+
5+
import (
6+
"context"
7+
"sync"
8+
)
9+
10+
// NewForTest builds a [Controller] in the requested state with the supplied
11+
// lifetime and guest seams installed.
12+
func NewForTest(state State, uvm vmLifetime, guest guestManager) *Controller {
13+
return &Controller{
14+
uvm: uvm,
15+
guest: guest,
16+
vmState: state,
17+
logOutputDone: make(chan struct{}),
18+
}
19+
}
20+
21+
// closeLogOutputOnce guards CloseLogOutputForTest from a double-close panic
22+
// when a single test exercises multiple paths that each finalize the log.
23+
var closeLogOutputOnce sync.Map
24+
25+
// CloseLogOutputForTest closes the logOutputDone channel so [Controller.Wait]
26+
// returns without blocking on real GCS log processing. Safe to call multiple
27+
// times against the same Controller.
28+
func CloseLogOutputForTest(c *Controller) {
29+
if _, loaded := closeLogOutputOnce.LoadOrStore(c, struct{}{}); loaded {
30+
return
31+
}
32+
close(c.logOutputDone)
33+
}
34+
35+
// WaitForVMExitForTest synchronously invokes [Controller.waitForVMExit] so
36+
// tests can drive the background-goroutine transition.
37+
func WaitForVMExitForTest(c *Controller, ctx context.Context) {
38+
c.waitForVMExit(ctx)
39+
}

0 commit comments

Comments
 (0)