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