v0.32.0 0617278

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
65// TestPrepareCEFSettings_LeavesCachePathToCEFDefault locks the P3 cache
66// contract: only RootCachePath is configured and CachePath stays empty, so
67// the global request context runs incognito-style with in-memory storage.
68// RootCachePath does not supply CachePath and grants no persistent
69// localStorage. Setting an explicit CachePath requires the P3.3
70// compatibility decision with data-preservation tests; see
71// docs/cef-cache-contract.md.
72func TestPrepareCEFSettings_LeavesCachePathToCEFDefault(t *testing.T) {
73	logger := zerolog.Nop()
74	profile := testCEFDevProfile(t)
75	settings, err := prepareCEFSettings(port.EngineOptions{}, RuntimePaths{StateRoot: profile.CEFUserDataDir(), LogFile: profile.CEFLogFile()}, RuntimeConfig{}, &logger)
76	require.NoError(t, err)
77	require.NotEmpty(t, settings.RootCachePath)
78	require.Empty(t, settings.CachePath, "explicit CachePath needs the P3.3 decision; empty CachePath means in-memory global context")
79}
80
81func TestParseLoadedLibCEFPath(t *testing.T) {
82	maps := "7f5fd4000000-7f5fd4200000 r--p 00000000 00:00 0 /usr/lib/libvulkan.so.1\n" +
83		"7f5fe0000000-7f5fe8200000 r-xp 00000000 00:00 0 /opt/cef-vaapi-runtime/usr/lib/cef/libcef.so\n" +
84		"7f5fe8200000-7f5fe8400000 r--p 08200000 00:00 0 /opt/cef-vaapi-runtime/usr/lib/cef/libcef.so\n"
85
86	require.Equal(t, "/opt/cef-vaapi-runtime/usr/lib/cef/libcef.so", parseLoadedLibCEFPath(maps))
87}
88
89func TestParseLoadedLibCEFPath_ReturnsEmptyWhenAbsent(t *testing.T) {
90	require.Empty(t, parseLoadedLibCEFPath("7f5fd4000000-7f5fd4200000 r--p 00000000 00:00 0 /usr/lib/libvulkan.so.1\n"))
91}
92
93func TestPrepareCEFSettings_RejectsNonDefaultCookiePolicy(t *testing.T) {
94	logger := zerolog.Nop()
95	profile := testCEFDevProfile(t)
96	_, err := prepareCEFSettings(port.EngineOptions{CookiePolicy: port.CookiePolicyNever}, RuntimePaths{StateRoot: profile.CEFUserDataDir(), LogFile: profile.CEFLogFile()}, RuntimeConfig{}, &logger)
97	require.ErrorIs(t, err, ErrCookiePolicyUnsupported)
98}
99
100func TestPrepareCEFSettings_AllowsNoThirdPartyCookiePolicy(t *testing.T) {
101	logger := zerolog.Nop()
102	profile := testCEFDevProfile(t)
103	_, err := prepareCEFSettings(port.EngineOptions{CookiePolicy: port.CookiePolicyNoThirdParty}, RuntimePaths{StateRoot: profile.CEFUserDataDir(), LogFile: profile.CEFLogFile()}, RuntimeConfig{}, &logger)
104	require.NoError(t, err)
105}
106
107func TestResolveCEFInitTraceFile_EmptyWithoutLogPath(t *testing.T) {
108	require.Empty(t, resolveCEFInitTraceFile("", ""))
109}
110
111func TestPrepareCEFLogFile_CreatesPrivateDirectoryAndFile(t *testing.T) {
112	logFile := filepath.Join(t.TempDir(), "private", "cef_runtime.log")
113
114	path, err := prepareCEFLogFile("", logFile)
115	require.NoError(t, err)
116	require.Equal(t, logFile, path)
117
118	dirInfo, err := os.Stat(filepath.Dir(logFile))
119	require.NoError(t, err)
120	require.Equal(t, os.FileMode(dirPerm), dirInfo.Mode().Perm())
121
122	fileInfo, err := os.Stat(logFile)
123	require.NoError(t, err)
124	require.Equal(t, os.FileMode(filePerm), fileInfo.Mode().Perm())
125}
126
127func TestPrepareCEFLogFile_TightensExistingFilePermissions(t *testing.T) {
128	logFile := filepath.Join(t.TempDir(), "private", "cef_runtime.log")
129	require.NoError(t, os.MkdirAll(filepath.Dir(logFile), dirPerm))
130	require.NoError(t, os.WriteFile(logFile, []byte("existing"), 0o644))
131
132	path, err := prepareCEFLogFile("", logFile)
133	require.NoError(t, err)
134	require.Equal(t, logFile, path)
135
136	fileInfo, err := os.Stat(logFile)
137	require.NoError(t, err)
138	require.Equal(t, os.FileMode(filePerm), fileInfo.Mode().Perm())
139}
140
141func TestPrepareCEFLogFile_RejectsSymlink(t *testing.T) {
142	target := filepath.Join(t.TempDir(), "target.log")
143	require.NoError(t, os.WriteFile(target, []byte("existing"), 0o644))
144
145	logFile := filepath.Join(t.TempDir(), "private", "cef_runtime.log")
146	require.NoError(t, os.MkdirAll(filepath.Dir(logFile), dirPerm))
147	require.NoError(t, os.Symlink(target, logFile))
148
149	path, err := prepareCEFLogFile("", logFile)
150	require.ErrorContains(t, err, "must not be a symlink")
151	require.Empty(t, path)
152
153	targetInfo, statErr := os.Stat(target)
154	require.NoError(t, statErr)
155	require.Equal(t, os.FileMode(0o644), targetInfo.Mode().Perm())
156}
157
158func TestPrepareCEFLogFile_RejectsDirectoryPath(t *testing.T) {
159	logFile := filepath.Join(t.TempDir(), "private", "cef_runtime.log")
160	require.NoError(t, os.MkdirAll(logFile, dirPerm))
161
162	path, err := prepareCEFLogFile("", logFile)
163	require.ErrorContains(t, err, "not a regular file")
164	require.Empty(t, path)
165}
166
167func TestResolveCEFInitTraceFile_UsesExplicitLogFile(t *testing.T) {
168	logFile := filepath.Join(t.TempDir(), "custom", "cef_runtime.log")
169
170	path := resolveCEFInitTraceFile("", logFile)
171	require.Equal(t, filepath.Join(filepath.Dir(logFile), "cef_runtime.bootstrap.log"), path)
172}
173
174func TestResolveCEFInitTraceFile_UsesResolvedProfileLogDirByDefault(t *testing.T) {
175	profile := testCEFDevProfile(t)
176
177	path := resolveCEFInitTraceFile(profile.CEFLogFile(), "")
178	require.Equal(t, filepath.Join(profile.EnginePaths.LogDir, "cef_runtime.bootstrap.log"), path)
179}
180
181func TestPrepareCEFSettings_IgnoresInitTraceRequestWithoutTouchingFilesystem(t *testing.T) {
182	t.Setenv(puregoCEFInitTraceEnvVar, "1")
183	profile := testCEFDevProfile(t)
184	bootstrapLogFile := resolveCEFInitTraceFile(profile.CEFLogFile(), "")
185
186	var logs bytes.Buffer
187	logger := zerolog.New(&logs)
188
189	settings, err := prepareCEFSettings(
190		port.EngineOptions{},
191		RuntimePaths{StateRoot: profile.CEFUserDataDir(), LogFile: profile.CEFLogFile()},
192		RuntimeConfig{},
193		&logger,
194	)
195	require.NoError(t, err)
196	require.Equal(t, profile.CEFLogFile(), settings.LogFile)
197
198	_, statErr := os.Stat(bootstrapLogFile)
199	require.ErrorIs(t, statErr, os.ErrNotExist)
200	require.Contains(t, logs.String(), puregoCEFInitTraceEnvVar)
201	require.Contains(t, logs.String(), bootstrapLogFile)
202}
203
204// TestCreateAudioOutputFactory_CreatesUsableFactory verifies that the audio
205// factory can be created and produces working streams. This tests the runtime
206// wiring decision to create the factory during CEF engine setup.
207func TestCreateAudioOutputFactory_ReturnsFactory(t *testing.T) {
208	audioFactory := factory.NewAudioOutputFactory()
209
210	require.NotNil(t, audioFactory, "Audio factory should not be nil")
211}
212
213func testCEFDevProfile(t *testing.T) runtimeprofile.Profile {
214	t.Helper()
215	cwd := t.TempDir()
216	runtimeDir := shortRuntimeDir(t)
217	t.Setenv("XDG_RUNTIME_DIR", runtimeDir)
218	profile, err := runtimeprofile.Resolve(runtimeprofile.ResolveInput{
219		Env: func(key string) string {
220			switch key {
221			case "ENV":
222				return "dev"
223			case "XDG_RUNTIME_DIR":
224				return runtimeDir
225			default:
226				return ""
227			}
228		},
229		Engine: "cef",
230		CWD:    func() (string, error) { return cwd, nil },
231	})
232	require.NoError(t, err)
233	return profile
234}
235
236func shortRuntimeDir(t *testing.T) string {
237	t.Helper()
238
239	dir, err := os.MkdirTemp("", "dbr-")
240	require.NoError(t, err)
241	t.Cleanup(func() {
242		_ = os.RemoveAll(dir)
243	})
244	return dir
245}
246
247func testCEFProdProfile(t *testing.T) runtimeprofile.Profile {
248	t.Helper()
249	root := t.TempDir()
250	profile, err := runtimeprofile.Resolve(runtimeprofile.ResolveInput{
251		Env:    func(string) string { return "" },
252		Engine: "cef",
253		CWD:    func() (string, error) { return root, nil },
254		Base: runtimeprofile.BasePaths{
255			ConfigHome: filepath.Join(root, "config"),
256			DataHome:   filepath.Join(root, "data"),
257			StateHome:  filepath.Join(root, "state"),
258			CacheHome:  filepath.Join(root, "cache"),
259		},
260	})
261	require.NoError(t, err)
262	return profile
263}

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