168 lines
5.1 KiB
Go
168 lines
5.1 KiB
Go
package windows
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestPlatformStubContract(t *testing.T) {
|
|
platform := New()
|
|
if platform.OS() == "" {
|
|
t.Fatal("OS() should not be empty")
|
|
}
|
|
if platform.Edition() != EditionModern {
|
|
t.Fatalf("Edition() = %q, want %q", platform.Edition(), EditionModern)
|
|
}
|
|
}
|
|
|
|
func TestEntrypointIsRunningUsesFullPathIdentity(t *testing.T) {
|
|
root := t.TempDir()
|
|
target := filepath.Join(root, "one", "App.exe")
|
|
collision := filepath.Join(root, "two", "App.exe")
|
|
|
|
running, err := entrypointIsRunning(target, func() (processSnapshot, error) {
|
|
return &scriptedSnapshot{items: []snapshotItem{{path: collision}}}, nil
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("entrypointIsRunning() error = %v", err)
|
|
}
|
|
if running {
|
|
t.Fatal("entrypointIsRunning() matched a same-basename executable in another directory")
|
|
}
|
|
|
|
running, err = entrypointIsRunning(target, func() (processSnapshot, error) {
|
|
return &scriptedSnapshot{items: []snapshotItem{{path: collision}, {path: target}}}, nil
|
|
})
|
|
if err != nil || !running {
|
|
t.Fatalf("entrypointIsRunning() = (%v, %v), want (true, nil)", running, err)
|
|
}
|
|
}
|
|
|
|
func TestEntrypointIsRunningFailsClosedForSnapshotErrors(t *testing.T) {
|
|
expected := errors.New("snapshot failed")
|
|
_, err := entrypointIsRunning("App.exe", func() (processSnapshot, error) {
|
|
return nil, expected
|
|
})
|
|
if !errors.Is(err, expected) {
|
|
t.Fatalf("entrypointIsRunning() error = %v, want %v", err, expected)
|
|
}
|
|
|
|
_, err = entrypointIsRunning("App.exe", func() (processSnapshot, error) {
|
|
return &scriptedSnapshot{items: []snapshotItem{{err: expected}}}, nil
|
|
})
|
|
if !errors.Is(err, expected) {
|
|
t.Fatalf("entrypointIsRunning() error = %v, want %v", err, expected)
|
|
}
|
|
}
|
|
|
|
func TestVersionSupports(t *testing.T) {
|
|
cases := []struct {
|
|
minOS string
|
|
major, minor, build, service uint32
|
|
want bool
|
|
wantErr bool
|
|
}{
|
|
{minOS: "windows-7-sp1", major: 6, minor: 1, service: 1, want: true},
|
|
{minOS: "windows-7-sp1", major: 6, minor: 1, service: 0, want: false},
|
|
{minOS: "windows-10", major: 10, want: true},
|
|
{minOS: "windows-11", major: 10, build: 19045, want: false},
|
|
{minOS: "windows-11", major: 10, build: 22000, want: true},
|
|
{minOS: "unknown", wantErr: true},
|
|
}
|
|
for _, test := range cases {
|
|
got, err := versionSupports(test.minOS, test.major, test.minor, test.build, test.service)
|
|
if (err != nil) != test.wantErr || got != test.want {
|
|
t.Fatalf("versionSupports(%q, %d, %d, %d, %d) = (%v, %v), want (%v, error=%v)", test.minOS, test.major, test.minor, test.build, test.service, got, err, test.want, test.wantErr)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWaitForExit(t *testing.T) {
|
|
initial := time.Date(2026, 7, 19, 0, 0, 0, 0, time.UTC)
|
|
errSnapshot := errors.New("snapshot failed")
|
|
tests := []struct {
|
|
name string
|
|
ctx context.Context
|
|
running []bool
|
|
runErr error
|
|
timeout time.Duration
|
|
wantErr error
|
|
wantSleeps int
|
|
}{
|
|
{name: "already stopped", ctx: context.Background(), running: []bool{false}, timeout: time.Second},
|
|
{name: "stops after one poll", ctx: context.Background(), running: []bool{true, false}, timeout: time.Second, wantSleeps: 1},
|
|
{name: "timeout", ctx: context.Background(), running: []bool{true, true, true, true, true}, timeout: time.Second, wantErr: context.DeadlineExceeded, wantSleeps: 4},
|
|
{name: "snapshot failure", ctx: context.Background(), runErr: errSnapshot, timeout: time.Second, wantErr: errSnapshot},
|
|
{name: "canceled", ctx: canceledWaitContext(), running: []bool{true}, timeout: time.Second, wantErr: context.Canceled},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
clock := &fakeExitWaitClock{now: initial}
|
|
index := 0
|
|
err := waitForExit(test.ctx, test.timeout, func() (bool, error) {
|
|
if test.runErr != nil {
|
|
return false, test.runErr
|
|
}
|
|
if index >= len(test.running) {
|
|
return test.running[len(test.running)-1], nil
|
|
}
|
|
running := test.running[index]
|
|
index++
|
|
return running, nil
|
|
}, clock)
|
|
if !errors.Is(err, test.wantErr) {
|
|
t.Fatalf("waitForExit() error = %v, want %v", err, test.wantErr)
|
|
}
|
|
if clock.sleeps != test.wantSleeps {
|
|
t.Fatalf("sleeps = %d, want %d", clock.sleeps, test.wantSleeps)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func canceledWaitContext() context.Context {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
return ctx
|
|
}
|
|
|
|
type fakeExitWaitClock struct {
|
|
now time.Time
|
|
sleeps int
|
|
}
|
|
|
|
func (clock *fakeExitWaitClock) Now() time.Time { return clock.now }
|
|
|
|
func (clock *fakeExitWaitClock) Wait(ctx context.Context, duration time.Duration) error {
|
|
if err := ctx.Err(); err != nil {
|
|
return err
|
|
}
|
|
clock.sleeps++
|
|
clock.now = clock.now.Add(duration)
|
|
return nil
|
|
}
|
|
|
|
type snapshotItem struct {
|
|
path string
|
|
err error
|
|
}
|
|
|
|
type scriptedSnapshot struct {
|
|
items []snapshotItem
|
|
next int
|
|
}
|
|
|
|
func (snapshot *scriptedSnapshot) NextImagePath() (string, bool, error) {
|
|
if snapshot.next == len(snapshot.items) {
|
|
return "", false, nil
|
|
}
|
|
item := snapshot.items[snapshot.next]
|
|
snapshot.next++
|
|
return item.path, true, item.err
|
|
}
|
|
|
|
func (*scriptedSnapshot) Close() error { return nil }
|