返回 DeepSeek-Reasonix
retry_windows_test.go
根目录 / internal / installlayout / retry_windows_test.go
1 //go:build windows
2
3 package installlayout
4
5 import (
6 "errors"
7 "os"
8 "path/filepath"
9 "testing"
10 "time"
11
12 "golang.org/x/sys/windows"
13 )
14
15 func silenceRetryDelay(t *testing.T) *[]time.Duration {
16 t.Helper()
17 restore := transientRetryDelay
18 var delays []time.Duration
19 transientRetryDelay = func(d time.Duration) { delays = append(delays, d) }
20 t.Cleanup(func() { transientRetryDelay = restore })
21 return &delays
22 }
23
24 func TestRetryTransientRecoversFromScannerLocks(t *testing.T) {
25 delays := silenceRetryDelay(t)
26 attempts := 0
27 err := retryTransient(func() error {
28 attempts++
29 if attempts < 4 {
30 return &os.PathError{Op: "rename", Path: "x", Err: windows.ERROR_SHARING_VIOLATION}
31 }
32 return nil
33 })
34 if err != nil || attempts != 4 || len(*delays) != 3 {
35 t.Fatalf("attempts=%d delays=%v err=%v", attempts, *delays, err)
36 }
37 if (*delays)[0] != 250*time.Millisecond || (*delays)[2] != 750*time.Millisecond {
38 t.Fatalf("backoff must grow linearly: %v", *delays)
39 }
40 }
41
42 func TestRetryTransientGivesUpAndKeepsTheErrno(t *testing.T) {
43 delays := silenceRetryDelay(t)
44 attempts := 0
45 err := retryTransient(func() error {
46 attempts++
47 return &os.PathError{Op: "remove", Path: "x", Err: windows.ERROR_ACCESS_DENIED}
48 })
49 if !errors.Is(err, windows.ERROR_ACCESS_DENIED) || attempts != transientRetryAttempts || len(*delays) != transientRetryAttempts-1 {
50 t.Fatalf("attempts=%d delays=%d err=%v", attempts, len(*delays), err)
51 }
52 }
53
54 // Windows refuses to rename a directory while any file inside it is open,
55 // which is how a scanner briefly blocks displacing an existing version tree.
56 func TestActivateVersionOutlastsAnOpenHandleInTheReplacedTree(t *testing.T) {
57 root := t.TempDir()
58 seed := ActivationRequest{InstallRoot: root, Version: "v1.20.0", RequestID: "seed", Members: writeMembers(t, t.TempDir(), "old")}
59 if err := ActivateVersion(seed); err != nil {
60 t.Fatal(err)
61 }
62 held, err := os.Open(filepath.Join(root, VersionsDirName, "v1.20.0", DesktopBinaryName()))
63 if err != nil {
64 t.Fatal(err)
65 }
66 released := make(chan struct{})
67 go func() {
68 time.Sleep(600 * time.Millisecond)
69 held.Close()
70 close(released)
71 }()
72 err = ActivateVersion(ActivationRequest{InstallRoot: root, Version: "v1.20.0", RequestID: "again", Members: writeMembers(t, t.TempDir(), "new")})
73 <-released
74 if err != nil {
75 t.Fatalf("activation must wait out a transient handle: %v", err)
76 }
77 data, err := os.ReadFile(filepath.Join(root, VersionsDirName, "v1.20.0", DesktopBinaryName()))
78 if err != nil || string(data) != "new-"+DesktopBinaryName() {
79 t.Fatalf("replacement not published: %q err=%v", data, err)
80 }
81 }
82
82 lines GO