v0.31.0 bf0756b

engine_init_test.go RawHistoryFolder

1package cef
2
3import (
4	"bytes"
5	"os"
6	"path/filepath"
7	"testing"
8
9	"github.com/bnema/dumber/internal/application/port"
10	"github.com/bnema/dumber/internal/infrastructure/audio/factory"
11	"github.com/bnema/dumber/internal/infrastructure/runtimeprofile"
12	"github.com/rs/zerolog"
13	"github.com/stretchr/testify/require"
14)
15
16func TestResolvedStateRoot_PrefersCacheDir(t *testing.T) {
17	profile := testCEFDevProfile(t)
18	root := resolvedStateRoot(profile.CEFUserDataDir(), port.EngineOptions{DataDir: "/tmp/data", CacheDir: "/tmp/cache"})
19	require.Equal(t, "/tmp/cache", root)
20}
21
22func TestResolvedStateRoot_UsesEnvOverride(t *testing.T) {
23	profile := testCEFDevProfile(t)
24	t.Setenv(CEFRootCachePathEnvVar, "/tmp/override")
25
26	root := resolvedStateRoot(profile.CEFUserDataDir(), port.EngineOptions{})
27	require.Equal(t, "/tmp/override", root)
28
29	root = resolvedStateRoot(profile.CEFUserDataDir(), port.EngineOptions{DataDir: "/tmp/data", CacheDir: "/tmp/cache"})
30	require.Equal(t, "/tmp/override", root)
31}
32
33func TestResolvedStateRoot_UsesProfileDefault(t *testing.T) {
34	profile := testCEFProdProfile(t)
35	root := resolvedStateRoot(profile.CEFUserDataDir(), port.EngineOptions{})
36	require.Equal(t, profile.CEFUserDataDir(), root)
37}
38
39func TestConfigureCEFSubprocessRuntime_PropagatesConfiguredRuntimeBeforeInit(t *testing.T) {
40	t.Setenv("CEF_DIR", "")
41
42	runtime := filepath.Join(t.TempDir(), "cef-147")
43	require.Equal(t, runtime, configureCEFSubprocessRuntime(runtime))
44	require.Equal(t, runtime, os.Getenv("CEF_DIR"))
45}
46
47func TestConfigureCEFSubprocessRuntime_PreservesExplicitEnvironmentOverride(t *testing.T) {
48	explicitRuntime := filepath.Join(t.TempDir(), "cef-env")
49	t.Setenv("CEF_DIR", explicitRuntime)
50
51	require.Equal(t, explicitRuntime, configureCEFSubprocessRuntime(filepath.Join(t.TempDir(), "cef-config")))
52	require.Equal(t, explicitRuntime, os.Getenv("CEF_DIR"))
53}
54
55func TestPrepareCEFSettings_UsesResolvedProfilePaths(t *testing.T) {
56	logger := zerolog.Nop()
57	profile := testCEFDevProfile(t)
58	settings, err := prepareCEFSettings(port.EngineOptions{}, RuntimePaths{StateRoot: profile.CEFUserDataDir(), LogFile: profile.CEFLogFile()}, RuntimeConfig{}, &logger)
59	require.NoError(t, err)
60	require.Equal(t, profile.CEFUserDataDir(), settings.RootCachePath)
61	require.Equal(t, profile.CEFLogFile(), settings.LogFile)
62	require.Empty(t, settings.BrowserSubprocessPath)
63}
64
65func TestParseLoadedLibCEFPath(t *testing.T) {
66	maps := "7f5fd4000000-7f5fd4200000 r--p 00000000 00:00 0 /usr/lib/libvulkan.so.1\n" +
67		"7f5fe0000000-7f5fe8200000 r-xp 00000000 00:00 0 /opt/cef-vaapi-runtime/usr/lib/cef/libcef.so\n" +
68		"7f5fe8200000-7f5fe8400000 r--p 08200000 00:00 0 /opt/cef-vaapi-runtime/usr/lib/cef/libcef.so\n"
69
70	require.Equal(t, "/opt/cef-vaapi-runtime/usr/lib/cef/libcef.so", parseLoadedLibCEFPath(maps))
71}
72
73func TestParseLoadedLibCEFPath_ReturnsEmptyWhenAbsent(t *testing.T) {
74	require.Empty(t, parseLoadedLibCEFPath("7f5fd4000000-7f5fd4200000 r--p 00000000 00:00 0 /usr/lib/libvulkan.so.1\n"))
75}
76
77func TestPrepareCEFSettings_RejectsNonDefaultCookiePolicy(t *testing.T) {
78	logger := zerolog.Nop()
79	profile := testCEFDevProfile(t)
80	_, err := prepareCEFSettings(port.EngineOptions{CookiePolicy: port.CookiePolicyNever}, RuntimePaths{StateRoot: profile.CEFUserDataDir(), LogFile: profile.CEFLogFile()}, RuntimeConfig{}, &logger)
81	require.ErrorIs(t, err, ErrCookiePolicyUnsupported)
82}
83
84func TestPrepareCEFSettings_AllowsNoThirdPartyCookiePolicy(t *testing.T) {
85	logger := zerolog.Nop()
86	profile := testCEFDevProfile(t)
87	_, err := prepareCEFSettings(port.EngineOptions{CookiePolicy: port.CookiePolicyNoThirdParty}, RuntimePaths{StateRoot: profile.CEFUserDataDir(), LogFile: profile.CEFLogFile()}, RuntimeConfig{}, &logger)
88	require.NoError(t, err)
89}
90
91func TestResolveCEFInitTraceFile_EmptyWithoutLogPath(t *testing.T) {
92	require.Empty(t, resolveCEFInitTraceFile("", ""))
93}
94
95func TestPrepareCEFLogFile_CreatesPrivateDirectoryAndFile(t *testing.T) {
96	logFile := filepath.Join(t.TempDir(), "private", "cef_runtime.log")
97
98	path, err := prepareCEFLogFile("", logFile)
99	require.NoError(t, err)
100	require.Equal(t, logFile, path)
101
102	dirInfo, err := os.Stat(filepath.Dir(logFile))
103	require.NoError(t, err)
104	require.Equal(t, os.FileMode(dirPerm), dirInfo.Mode().Perm())
105
106	fileInfo, err := os.Stat(logFile)
107	require.NoError(t, err)
108	require.Equal(t, os.FileMode(filePerm), fileInfo.Mode().Perm())
109}
110
111func TestPrepareCEFLogFile_TightensExistingFilePermissions(t *testing.T) {
112	logFile := filepath.Join(t.TempDir(), "private", "cef_runtime.log")
113	require.NoError(t, os.MkdirAll(filepath.Dir(logFile), dirPerm))
114	require.NoError(t, os.WriteFile(logFile, []byte("existing"), 0o644))
115
116	path, err := prepareCEFLogFile("", logFile)
117	require.NoError(t, err)
118	require.Equal(t, logFile, path)
119
120	fileInfo, err := os.Stat(logFile)
121	require.NoError(t, err)
122	require.Equal(t, os.FileMode(filePerm), fileInfo.Mode().Perm())
123}
124
125func TestPrepareCEFLogFile_RejectsSymlink(t *testing.T) {
126	target := filepath.Join(t.TempDir(), "target.log")
127	require.NoError(t, os.WriteFile(target, []byte("existing"), 0o644))
128
129	logFile := filepath.Join(t.TempDir(), "private", "cef_runtime.log")
130	require.NoError(t, os.MkdirAll(filepath.Dir(logFile), dirPerm))
131	require.NoError(t, os.Symlink(target, logFile))
132
133	path, err := prepareCEFLogFile("", logFile)
134	require.ErrorContains(t, err, "must not be a symlink")
135	require.Empty(t, path)
136
137	targetInfo, statErr := os.Stat(target)
138	require.NoError(t, statErr)
139	require.Equal(t, os.FileMode(0o644), targetInfo.Mode().Perm())
140}
141
142func TestPrepareCEFLogFile_RejectsDirectoryPath(t *testing.T) {
143	logFile := filepath.Join(t.TempDir(), "private", "cef_runtime.log")
144	require.NoError(t, os.MkdirAll(logFile, dirPerm))
145
146	path, err := prepareCEFLogFile("", logFile)
147	require.ErrorContains(t, err, "not a regular file")
148	require.Empty(t, path)
149}
150
151func TestResolveCEFInitTraceFile_UsesExplicitLogFile(t *testing.T) {
152	logFile := filepath.Join(t.TempDir(), "custom", "cef_runtime.log")
153
154	path := resolveCEFInitTraceFile("", logFile)
155	require.Equal(t, filepath.Join(filepath.Dir(logFile), "cef_runtime.bootstrap.log"), path)
156}
157
158func TestResolveCEFInitTraceFile_UsesResolvedProfileLogDirByDefault(t *testing.T) {
159	profile := testCEFDevProfile(t)
160
161	path := resolveCEFInitTraceFile(profile.CEFLogFile(), "")
162	require.Equal(t, filepath.Join(profile.EnginePaths.LogDir, "cef_runtime.bootstrap.log"), path)
163}
164
165func TestPrepareCEFSettings_IgnoresInitTraceRequestWithoutTouchingFilesystem(t *testing.T) {
166	t.Setenv(puregoCEFInitTraceEnvVar, "1")
167	profile := testCEFDevProfile(t)
168	bootstrapLogFile := resolveCEFInitTraceFile(profile.CEFLogFile(), "")
169
170	var logs bytes.Buffer
171	logger := zerolog.New(&logs)
172
173	settings, err := prepareCEFSettings(
174		port.EngineOptions{},
175		RuntimePaths{StateRoot: profile.CEFUserDataDir(), LogFile: profile.CEFLogFile()},
176		RuntimeConfig{},
177		&logger,
178	)
179	require.NoError(t, err)
180	require.Equal(t, profile.CEFLogFile(), settings.LogFile)
181
182	_, statErr := os.Stat(bootstrapLogFile)
183	require.ErrorIs(t, statErr, os.ErrNotExist)
184	require.Contains(t, logs.String(), puregoCEFInitTraceEnvVar)
185	require.Contains(t, logs.String(), bootstrapLogFile)
186}
187
188// TestCreateAudioOutputFactory_CreatesUsableFactory verifies that the audio
189// factory can be created and produces working streams. This tests the runtime
190// wiring decision to create the factory during CEF engine setup.
191func TestCreateAudioOutputFactory_ReturnsFactory(t *testing.T) {
192	audioFactory := factory.NewAudioOutputFactory()
193
194	require.NotNil(t, audioFactory, "Audio factory should not be nil")
195}
196
197func testCEFDevProfile(t *testing.T) runtimeprofile.Profile {
198	t.Helper()
199	cwd := t.TempDir()
200	runtimeDir := shortRuntimeDir(t)
201	t.Setenv("XDG_RUNTIME_DIR", runtimeDir)
202	profile, err := runtimeprofile.Resolve(runtimeprofile.ResolveInput{
203		Env: func(key string) string {
204			switch key {
205			case "ENV":
206				return "dev"
207			case "XDG_RUNTIME_DIR":
208				return runtimeDir
209			default:
210				return ""
211			}
212		},
213		Engine: "cef",
214		CWD:    func() (string, error) { return cwd, nil },
215	})
216	require.NoError(t, err)
217	return profile
218}
219
220func shortRuntimeDir(t *testing.T) string {
221	t.Helper()
222
223	dir, err := os.MkdirTemp("", "dbr-")
224	require.NoError(t, err)
225	t.Cleanup(func() {
226		_ = os.RemoveAll(dir)
227	})
228	return dir
229}
230
231func testCEFProdProfile(t *testing.T) runtimeprofile.Profile {
232	t.Helper()
233	root := t.TempDir()
234	profile, err := runtimeprofile.Resolve(runtimeprofile.ResolveInput{
235		Env:    func(string) string { return "" },
236		Engine: "cef",
237		CWD:    func() (string, error) { return root, nil },
238		Base: runtimeprofile.BasePaths{
239			ConfigHome: filepath.Join(root, "config"),
240			DataHome:   filepath.Join(root, "data"),
241			StateHome:  filepath.Join(root, "state"),
242			CacheHome:  filepath.Join(root, "cache"),
243		},
244	})
245	require.NoError(t, err)
246	return profile
247}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close