1package env_test
2
3import (
4 "context"
5 "os"
6 "os/exec"
7 "testing"
8
9 "github.com/bnema/dumber/internal/infrastructure/env"
10 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/require"
12)
13
14func TestManager_ApplyEnvironment_SkiaEnv(t *testing.T) {
15 // Save original env vars and restore after test
16 origCPU := os.Getenv("WEBKIT_SKIA_CPU_PAINTING_THREADS")
17 origGPU := os.Getenv("WEBKIT_SKIA_GPU_PAINTING_THREADS")
18 origCPURender := os.Getenv("WEBKIT_SKIA_ENABLE_CPU_RENDERING")
19 t.Cleanup(func() {
20 restoreEnv("WEBKIT_SKIA_CPU_PAINTING_THREADS", origCPU)
21 restoreEnv("WEBKIT_SKIA_GPU_PAINTING_THREADS", origGPU)
22 restoreEnv("WEBKIT_SKIA_ENABLE_CPU_RENDERING", origCPURender)
23 })
24
25 tests := []struct {
26 name string
27 settings env.RenderingSettings
28 presetEnv map[string]string
29 expectedVars map[string]string
30 unexpectedVars []string
31 }{
32 {
33 name: "CPU threads set when > 0",
34 settings: env.RenderingSettings{
35 SkiaCPUPaintingThreads: 4,
36 SkiaGPUPaintingThreads: -1, // unset
37 },
38 expectedVars: map[string]string{
39 "WEBKIT_SKIA_CPU_PAINTING_THREADS": "4",
40 },
41 unexpectedVars: []string{"WEBKIT_SKIA_GPU_PAINTING_THREADS"},
42 },
43 {
44 name: "GPU threads set when >= 0",
45 settings: env.RenderingSettings{
46 SkiaCPUPaintingThreads: 0, // unset
47 SkiaGPUPaintingThreads: 2,
48 },
49 expectedVars: map[string]string{
50 "WEBKIT_SKIA_GPU_PAINTING_THREADS": "2",
51 },
52 unexpectedVars: []string{"WEBKIT_SKIA_CPU_PAINTING_THREADS"},
53 },
54 {
55 name: "GPU threads 0 disables GPU tile painting",
56 settings: env.RenderingSettings{
57 SkiaGPUPaintingThreads: 0,
58 },
59 expectedVars: map[string]string{
60 "WEBKIT_SKIA_GPU_PAINTING_THREADS": "0",
61 },
62 },
63 {
64 name: "CPU rendering enabled",
65 settings: env.RenderingSettings{
66 SkiaEnableCPURendering: true,
67 },
68 expectedVars: map[string]string{
69 "WEBKIT_SKIA_ENABLE_CPU_RENDERING": "1",
70 },
71 },
72 {
73 name: "CPU rendering disabled (default)",
74 settings: env.RenderingSettings{
75 SkiaEnableCPURendering: false,
76 },
77 unexpectedVars: []string{"WEBKIT_SKIA_ENABLE_CPU_RENDERING"},
78 },
79 {
80 name: "does not override existing CPU threads env var",
81 settings: env.RenderingSettings{
82 SkiaCPUPaintingThreads: 8,
83 },
84 presetEnv: map[string]string{
85 "WEBKIT_SKIA_CPU_PAINTING_THREADS": "2",
86 },
87 expectedVars: map[string]string{
88 "WEBKIT_SKIA_CPU_PAINTING_THREADS": "2", // keeps original
89 },
90 },
91 {
92 name: "does not override existing GPU threads env var",
93 settings: env.RenderingSettings{
94 SkiaGPUPaintingThreads: 4,
95 },
96 presetEnv: map[string]string{
97 "WEBKIT_SKIA_GPU_PAINTING_THREADS": "1",
98 },
99 expectedVars: map[string]string{
100 "WEBKIT_SKIA_GPU_PAINTING_THREADS": "1", // keeps original
101 },
102 },
103 {
104 name: "does not override existing CPU rendering env var",
105 settings: env.RenderingSettings{
106 SkiaEnableCPURendering: true,
107 },
108 presetEnv: map[string]string{
109 "WEBKIT_SKIA_ENABLE_CPU_RENDERING": "0",
110 },
111 expectedVars: map[string]string{
112 "WEBKIT_SKIA_ENABLE_CPU_RENDERING": "0", // keeps original
113 },
114 },
115 {
116 name: "all unset by default",
117 settings: env.RenderingSettings{
118 SkiaCPUPaintingThreads: 0, // unset
119 SkiaGPUPaintingThreads: -1, // unset
120 SkiaEnableCPURendering: false,
121 },
122 unexpectedVars: []string{
123 "WEBKIT_SKIA_CPU_PAINTING_THREADS",
124 "WEBKIT_SKIA_GPU_PAINTING_THREADS",
125 "WEBKIT_SKIA_ENABLE_CPU_RENDERING",
126 },
127 },
128 }
129
130 for _, tt := range tests {
131 t.Run(tt.name, func(t *testing.T) {
132 // Clear env vars before each test
133 os.Unsetenv("WEBKIT_SKIA_CPU_PAINTING_THREADS")
134 os.Unsetenv("WEBKIT_SKIA_GPU_PAINTING_THREADS")
135 os.Unsetenv("WEBKIT_SKIA_ENABLE_CPU_RENDERING")
136
137 // Set any preset env vars
138 for k, v := range tt.presetEnv {
139 os.Setenv(k, v)
140 }
141
142 manager := env.NewManager()
143 ctx := context.Background()
144
145 err := manager.ApplyEnvironment(ctx, tt.settings)
146 require.NoError(t, err)
147
148 // Check expected vars are set correctly
149 for k, v := range tt.expectedVars {
150 actual := os.Getenv(k)
151 assert.Equal(t, v, actual, "env var %s", k)
152 }
153
154 // Check unexpected vars are not set (or were not set by us)
155 appliedVars := manager.GetAppliedVars()
156 for _, k := range tt.unexpectedVars {
157 _, wasApplied := appliedVars[k]
158 if tt.presetEnv[k] == "" {
159 assert.False(t, wasApplied, "env var %s should not be applied", k)
160 }
161 }
162 })
163 }
164}
165
166func restoreEnv(key, value string) {
167 if value == "" {
168 os.Unsetenv(key)
169 } else {
170 os.Setenv(key, value)
171 }
172}
173
174func TestIsFlatpak(t *testing.T) {
175 // IsFlatpak checks for /.flatpak-info file existence
176 // In normal test environment, this should return false
177 result := env.IsFlatpak()
178
179 // We can't easily mock filesystem, so just verify it returns a boolean
180 // and doesn't panic. In CI/dev environment it should be false.
181 assert.False(t, result, "should not detect Flatpak in test environment")
182}
183
184func TestIsPacman(t *testing.T) {
185 // Only run these tests on Arch-based systems with pacman
186 if _, err := os.Stat("/usr/bin/pacman"); os.IsNotExist(err) {
187 t.Skip("pacman not found, skipping Arch-specific tests")
188 }
189
190 tests := []struct {
191 name string
192 binary string
193 expected bool
194 }{
195 {
196 name: "system binary owned by pacman",
197 binary: "/usr/bin/bash",
198 expected: true, // bash is always installed via pacman on Arch
199 },
200 {
201 name: "non-existent binary",
202 binary: "/nonexistent/path/to/binary",
203 expected: false,
204 },
205 }
206
207 for _, tt := range tests {
208 t.Run(tt.name, func(t *testing.T) {
209 // We can't easily test IsPacman directly since it uses os.Executable()
210 // but we can test the underlying pacman -Qo logic with exec.Command
211 cmd := exec.Command("pacman", "-Qo", tt.binary)
212 err := cmd.Run()
213 if tt.expected {
214 assert.NoError(t, err, "pacman should own %s", tt.binary)
215 } else {
216 assert.Error(t, err, "pacman should not own %s", tt.binary)
217 }
218 })
219 }
220}
221
222func TestIsPacman_NonArchSystem(t *testing.T) {
223 // On non-Arch systems, IsPacman should return false (pacman not found)
224 if _, err := os.Stat("/usr/bin/pacman"); err == nil {
225 t.Skip("pacman found, skipping non-Arch test")
226 }
227
228 result := env.IsPacman()
229 assert.False(t, result, "should return false when pacman is not installed")
230}