package config import ( "os" "path/filepath" "testing" "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) const oldFormatConfig = ` [performance] profile = "balanced" zoom_cache_size = 50 webview_pool_prewarm_count = 2 skia_cpu_painting_threads = 4 skia_gpu_painting_threads = 2 skia_enable_cpu_rendering = false web_process_memory_limit_mb = 512 web_process_memory_poll_interval_sec = 5.0 web_process_memory_conservative_threshold = 0.7 web_process_memory_strict_threshold = 0.9 network_process_memory_limit_mb = 256 network_process_memory_poll_interval_sec = 10.0 network_process_memory_conservative_threshold = 0.6 network_process_memory_strict_threshold = 0.85 [privacy] cookie_policy = "accept_all" itp_enabled = true [rendering] mode = "auto" disable_dmabuf_renderer = false force_compositing_mode = true disable_compositing_mode = false gsk_renderer = "gl" disable_mipmaps = false prefer_gl = true draw_compositing_indicators = false show_fps = false sample_memory = false debug_frames = false [media] hardware_decoding = true prefer_av1 = false show_diagnostics = false force_vsync = true gl_rendering_mode = "auto" gstreamer_debug_level = 2 [runtime] prefix = "/usr/local" ` func TestMigrateToEngineConfig_OldFormat(t *testing.T) { tmpDir := t.TempDir() configFile := filepath.Join(tmpDir, "config.toml") err := os.WriteFile(configFile, []byte(oldFormatConfig), 0o644) require.NoError(t, err) migrated, err := MigrateToEngineConfig(configFile) require.NoError(t, err) assert.True(t, migrated, "should report migration performed") // Read the migrated config data, err := os.ReadFile(configFile) require.NoError(t, err) var raw map[string]any err = toml.Unmarshal(data, &raw) require.NoError(t, err) // [engine] section must exist engineAny, ok := raw["engine"] require.True(t, ok, "engine section must exist") engine, ok := engineAny.(map[string]any) require.True(t, ok, "engine must be a map") // legacy engine migration preserves WebKit because the old rendering/media sections are WebKit-specific assert.Equal(t, "webkit", engine["type"]) // [performance] -> [engine] universal fields assert.Equal(t, "balanced", engine["profile"]) assert.Equal(t, int64(50), engine["zoom_cache_size"]) assert.Equal(t, int64(2), engine["pool_prewarm_count"]) // [privacy] -> [engine] universal field, translated to canonical schema value assert.Equal(t, "always", engine["cookie_policy"]) // [engine.webkit] section webkitAny, ok := engine["webkit"] require.True(t, ok, "engine.webkit section must exist") webkit, ok := webkitAny.(map[string]any) require.True(t, ok, "engine.webkit must be a map") // [privacy] -> [engine.webkit] assert.Equal(t, true, webkit["itp_enabled"]) // [rendering] -> [engine.webkit] (excluding mode which is dropped) assert.Equal(t, false, webkit["disable_dmabuf_renderer"]) assert.Equal(t, true, webkit["force_compositing_mode"]) assert.Equal(t, false, webkit["disable_compositing_mode"]) assert.Equal(t, "gl", webkit["gsk_renderer"]) assert.Equal(t, false, webkit["disable_mipmaps"]) assert.Equal(t, true, webkit["prefer_gl"]) assert.Equal(t, false, webkit["draw_compositing_indicators"]) assert.Equal(t, false, webkit["show_fps"]) assert.Equal(t, false, webkit["sample_memory"]) assert.Equal(t, false, webkit["debug_frames"]) // rendering.mode must be dropped (not present in webkit) assert.NotContains(t, webkit, "mode") // [performance] -> [engine.webkit] (webkit-specific) assert.Equal(t, int64(4), webkit["skia_cpu_painting_threads"]) assert.Equal(t, int64(2), webkit["skia_gpu_painting_threads"]) assert.Equal(t, false, webkit["skia_enable_cpu_rendering"]) assert.Equal(t, int64(512), webkit["web_process_memory_limit_mb"]) assert.InDelta(t, 5.0, webkit["web_process_memory_poll_interval_sec"], 0.001) assert.InDelta(t, 0.7, webkit["web_process_memory_conservative_threshold"], 0.001) assert.InDelta(t, 0.9, webkit["web_process_memory_strict_threshold"], 0.001) assert.Equal(t, int64(256), webkit["network_process_memory_limit_mb"]) assert.InDelta(t, 10.0, webkit["network_process_memory_poll_interval_sec"], 0.001) assert.InDelta(t, 0.6, webkit["network_process_memory_conservative_threshold"], 0.001) assert.InDelta(t, 0.85, webkit["network_process_memory_strict_threshold"], 0.001) // [media] GStreamer fields -> [engine.webkit] assert.Equal(t, true, webkit["force_vsync"]) assert.Equal(t, "auto", webkit["gl_rendering_mode"]) assert.Equal(t, int64(2), webkit["gstreamer_debug_level"]) // [runtime] -> [engine.webkit] assert.Equal(t, "/usr/local", webkit["prefix"]) // Old top-level sections must be removed assert.NotContains(t, raw, "performance") assert.NotContains(t, raw, "rendering") assert.NotContains(t, raw, "runtime") // [privacy] should be removed (all fields migrated) assert.NotContains(t, raw, "privacy") } func TestMigrateToEngineConfig_AlreadyMigrated(t *testing.T) { tmpDir := t.TempDir() configFile := filepath.Join(tmpDir, "config.toml") alreadyMigrated := ` [engine] type = "webkit" profile = "balanced" [engine.webkit] itp_enabled = true ` err := os.WriteFile(configFile, []byte(alreadyMigrated), 0o644) require.NoError(t, err) migrated, err := MigrateToEngineConfig(configFile) require.NoError(t, err) assert.False(t, migrated, "should not migrate when already migrated") // Content should be unchanged data, err := os.ReadFile(configFile) require.NoError(t, err) assert.Equal(t, alreadyMigrated, string(data)) } func TestMigrateToEngineConfig_NoOldSections(t *testing.T) { tmpDir := t.TempDir() configFile := filepath.Join(tmpDir, "config.toml") noOldSections := ` [history] max_entries = 1000 [logging] level = "info" ` err := os.WriteFile(configFile, []byte(noOldSections), 0o644) require.NoError(t, err) migrated, err := MigrateToEngineConfig(configFile) require.NoError(t, err) assert.False(t, migrated, "should not migrate when no old sections exist") // Content should be unchanged data, err := os.ReadFile(configFile) require.NoError(t, err) assert.Equal(t, noOldSections, string(data)) } func TestMigrateToEngineConfig_PreservesNonMigratedMediaFields(t *testing.T) { tmpDir := t.TempDir() configFile := filepath.Join(tmpDir, "config.toml") content := ` [media] hardware_decoding = true prefer_av1 = false show_diagnostics = true force_vsync = true gl_rendering_mode = "auto" gstreamer_debug_level = 3 [rendering] disable_dmabuf_renderer = false ` err := os.WriteFile(configFile, []byte(content), 0o644) require.NoError(t, err) migrated, err := MigrateToEngineConfig(configFile) require.NoError(t, err) assert.True(t, migrated) data, err := os.ReadFile(configFile) require.NoError(t, err) var raw map[string]any err = toml.Unmarshal(data, &raw) require.NoError(t, err) // media section must still exist with non-migrated fields mediaAny, ok := raw["media"] require.True(t, ok, "media section must still exist") media, ok := mediaAny.(map[string]any) require.True(t, ok) assert.Equal(t, true, media["hardware_decoding"]) assert.Equal(t, false, media["prefer_av1"]) assert.Equal(t, true, media["show_diagnostics"]) // Migrated media fields should be removed from media assert.NotContains(t, media, "force_vsync") assert.NotContains(t, media, "gl_rendering_mode") assert.NotContains(t, media, "gstreamer_debug_level") // Migrated fields should be in engine.webkit engineAny, ok := raw["engine"] require.True(t, ok, "engine section must exist") engine, ok := engineAny.(map[string]any) require.True(t, ok, "engine section must be a map") webkitAny, ok := engine["webkit"] require.True(t, ok, "engine.webkit section must exist") webkit, ok := webkitAny.(map[string]any) require.True(t, ok, "engine.webkit section must be a map") assert.Equal(t, true, webkit["force_vsync"]) assert.Equal(t, "auto", webkit["gl_rendering_mode"]) assert.Equal(t, int64(3), webkit["gstreamer_debug_level"]) } func TestLegacyEngineAliasesMetadataCoverage(t *testing.T) { require.NotEmpty(t, legacyEngineAliases) aliases := make(map[string]legacyEngineAlias) for _, alias := range legacyEngineAliases { key := alias.legacyKey() require.NotEmpty(t, alias.legacySection, "%s should have a legacy section", key) require.NotEmpty(t, alias.legacyField, "%s should have a legacy field", key) require.NotContains(t, aliases, key, "duplicate metadata for %s", key) aliases[key] = alias if alias.dropped { assert.Empty(t, alias.targetSection, "%s is dropped and should not have a target section", key) assert.Empty(t, alias.targetField, "%s is dropped and should not have a target field", key) continue } require.Contains(t, []string{"engine", "engine.webkit"}, alias.targetSection, "%s should target a known engine section", key) require.NotEmpty(t, alias.targetField, "%s should have a canonical target field", key) } for _, key := range []string{ "media.force_vsync", "media.gl_rendering_mode", "media.gstreamer_debug_level", "runtime.prefix", "performance.profile", "privacy.cookie_policy", "privacy.itp_enabled", "rendering.disable_dmabuf_renderer", } { alias, ok := aliases[key] require.True(t, ok, "missing metadata for %s", key) require.False(t, alias.dropped, "%s should have a canonical target", key) } renderingMode, ok := aliases["rendering.mode"] require.True(t, ok, "missing dropped metadata for rendering.mode") assert.True(t, renderingMode.dropped) } func TestHasOldEngineSections_UsesMetadataForMigratedMediaKeys(t *testing.T) { for _, field := range []string{"force_vsync", "gl_rendering_mode", "gstreamer_debug_level"} { raw := map[string]any{"media": map[string]any{field: true}} assert.True(t, hasOldEngineSections(raw), "media.%s should trigger engine migration", field) } raw := map[string]any{"media": map[string]any{"hardware_decoding": true}} assert.False(t, hasOldEngineSections(raw), "non-migrated media keys should not trigger engine migration") } func TestMigrateToEngineConfig_MixedEngineAndMigratedMediaKey(t *testing.T) { tmpDir := t.TempDir() configFile := filepath.Join(tmpDir, "config.toml") content := ` [engine] type = "webkit" [media] gl_rendering_mode = "auto" ` err := os.WriteFile(configFile, []byte(content), 0o644) require.NoError(t, err) migrated, err := MigrateToEngineConfig(configFile) require.Error(t, err) assert.False(t, migrated) assert.Contains(t, err.Error(), "media.gl_rendering_mode") } func TestMigrateToEngineConfig_EngineTypeDefault(t *testing.T) { tmpDir := t.TempDir() configFile := filepath.Join(tmpDir, "config.toml") // Minimal old-format config to trigger migration content := ` [rendering] show_fps = true ` err := os.WriteFile(configFile, []byte(content), 0o644) require.NoError(t, err) migrated, err := MigrateToEngineConfig(configFile) require.NoError(t, err) assert.True(t, migrated) data, err := os.ReadFile(configFile) require.NoError(t, err) var raw map[string]any err = toml.Unmarshal(data, &raw) require.NoError(t, err) engineAny, ok := raw["engine"] require.True(t, ok, "raw[\"engine\"] should exist") engine, ok := engineAny.(map[string]any) require.True(t, ok, "raw[\"engine\"] should be map[string]any, got %T", engineAny) assert.Equal(t, EngineTypeWebKit, engine["type"], "legacy engine migration should preserve WebKit") }