返回 DeepSeek-Reasonix
home.go
根目录 / internal / testenv / home.go
1 package testenv
2
3 import (
4 "fmt"
5 "os"
6 "path/filepath"
7 "sync"
8 )
9
10 // TestingM is the subset of testing.M needed by RunWithIsolatedUserState.
11 type TestingM interface {
12 Run() int
13 }
14
15 type savedEnvironment struct {
16 value string
17 set bool
18 }
19
20 // IsolateUserState redirects default user-scoped Reasonix paths to a disposable
21 // home and clears inherited explicit path overrides. Tests may still override
22 // any of these variables for a focused scenario after this process-level guard
23 // is installed.
24 func IsolateUserState() (func(), error) {
25 originalHome, _ := os.UserHomeDir()
26 home, err := os.MkdirTemp(originalHome, ".reasonix-test-home-*")
27 if err != nil && originalHome != "" {
28 home, err = os.MkdirTemp("", "reasonix-test-home-*")
29 }
30 if err != nil {
31 return nil, fmt.Errorf("create isolated test home: %w", err)
32 }
33
34 set := map[string]string{
35 "HOME": home,
36 "USERPROFILE": home,
37 "XDG_CONFIG_HOME": filepath.Join(home, ".config"),
38 "XDG_CACHE_HOME": filepath.Join(home, ".cache"),
39 "XDG_STATE_HOME": filepath.Join(home, ".local", "state"),
40 "AppData": filepath.Join(home, "AppData", "Roaming"),
41 "LocalAppData": filepath.Join(home, "AppData", "Local"),
42 }
43 unset := []string{
44 "REASONIX_HOME",
45 "REASONIX_STATE_HOME",
46 "REASONIX_CACHE_HOME",
47 }
48
49 saved := make(map[string]savedEnvironment, len(set)+len(unset))
50 remember := func(key string) {
51 if _, ok := saved[key]; ok {
52 return
53 }
54 value, ok := os.LookupEnv(key)
55 saved[key] = savedEnvironment{value: value, set: ok}
56 }
57 restore := func() {
58 for key, previous := range saved {
59 if previous.set {
60 _ = os.Setenv(key, previous.value)
61 } else {
62 _ = os.Unsetenv(key)
63 }
64 }
65 }
66 fail := func(cause error) (func(), error) {
67 restore()
68 _ = os.RemoveAll(home)
69 return nil, cause
70 }
71
72 for key, value := range set {
73 remember(key)
74 if err := os.Setenv(key, value); err != nil {
75 return fail(fmt.Errorf("set %s for isolated test home: %w", key, err))
76 }
77 }
78 for _, key := range unset {
79 remember(key)
80 if err := os.Unsetenv(key); err != nil {
81 return fail(fmt.Errorf("unset %s for isolated test home: %w", key, err))
82 }
83 }
84
85 var once sync.Once
86 return func() {
87 once.Do(func() {
88 restore()
89 _ = os.RemoveAll(home)
90 })
91 }, nil
92 }
93
94 // RunWithIsolatedUserState runs a package test binary behind the user-state
95 // isolation guard and exits with the test result.
96 func RunWithIsolatedUserState(m TestingM) {
97 cleanup, err := IsolateUserState()
98 if err != nil {
99 fmt.Fprintln(os.Stderr, err)
100 os.Exit(1)
101 }
102 exitCode := m.Run()
103 cleanup()
104 os.Exit(exitCode)
105 }
106
106 lines GO