// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package config import ( "os" "path/filepath" "strings" "testing" ) func TestApplicationDocumentSparseInheritanceAndExplicitValues(t *testing.T) { path := filepath.Join(t.TempDir(), "applications", "ninjam.toml") base := Defaults() doc, err := Open_application_document(path, base) if err != nil { t.Fatal(err) } saved := base saved.Presentation.Speech.Rate = 12 saved.Keyboard.WordEcho = true if err := doc.Save(saved); err != nil { t.Fatal(err) } data, _ := os.ReadFile(path) if strings.Contains(string(data), "driver") || strings.Contains(string(data), "automatic") || strings.Contains(string(data), "version") { t.Fatal("inherited values copied", string(data)) } info, _ := os.Stat(path) if info.Mode().Perm() != 0600 { t.Fatal(info.Mode()) } // A new ordinary voice is inherited; explicitly saved rate/echo survive. base.Presentation.Speech.Voice = "New default" base.Presentation.Speech.Rate = 12 doc, err = Open_application_document(path, base) if err != nil || doc.Settings.Presentation.Speech.Voice != "New default" || !doc.Settings.Keyboard.WordEcho { t.Fatal(doc, err) } if err := doc.Save(doc.Settings); err != nil { t.Fatal(err) } base.Presentation.Speech.Rate = 60 doc, err = Open_application_document(path, base) if err != nil || doc.Settings.Presentation.Speech.Rate != 12 { t.Fatal("explicit value equal to previous base lost", doc, err) } session := base session.Presentation.Speech.Enabled = false session.Reading.Automatic = false effective := doc.Apply_application(session) if effective.Presentation.Speech.Enabled || effective.Reading.Automatic || effective.Presentation.Speech.Rate != 12 { t.Fatal("session state overwritten", effective) } } func TestApplicationDocumentRejectsInvalidProfilesAndExternalChanges(t *testing.T) { path := filepath.Join(t.TempDir(), "profile.toml") for _, data := range []string{ "[presentation.speech]\nrate = 101\n", "[presentation.speech]\nrate = 'fast'\n", "[presentation.speech]\ndriver = 'dectalk'\n", "[keyboard]\nlayout = 'laptop'\n", "[screen]\nignored_vts = [1]\n", "[reading]\nunknown = true\n", "[reading]\nmode = 'invalid'\n", "[broken", } { if err := os.WriteFile(path, []byte(data), 0600); err != nil { t.Fatal(err) } if _, err := Open_application_document(path, Defaults()); err == nil { t.Fatal("invalid profile accepted", data) } } if err := os.WriteFile(path, []byte("[presentation.speech]\nrate = 10\n"), 0600); err != nil { t.Fatal(err) } doc, err := Open_application_document(path, Defaults()) if err != nil { t.Fatal(err) } changed := []byte("[presentation.speech]\nrate = 20\n") if err := os.WriteFile(path, changed, 0600); err != nil { t.Fatal(err) } if err := doc.Save(doc.Settings); err == nil { t.Fatal("external edit overwritten") } data, _ := os.ReadFile(path) if string(data) != string(changed) { t.Fatal(string(data)) } } func TestApplicationVoiceSelectionSavedTogether(t *testing.T) { path := filepath.Join(t.TempDir(), "profile.toml") base := Defaults() base.Presentation.Speech.Module = "original-module" base.Presentation.Speech.Language = "en" doc, err := Open_application_document(path, base) if err != nil { t.Fatal(err) } chosen := base chosen.Presentation.Speech.Voice = "Application voice" if err := doc.Save(chosen); err != nil { t.Fatal(err) } base.Presentation.Speech.Module = "different-module" base.Presentation.Speech.Language = "fr" doc, err = Open_application_document(path, base) if err != nil || doc.Settings.Presentation.Speech.Module != "original-module" || doc.Settings.Presentation.Speech.Language != "en" || doc.Settings.Presentation.Speech.Voice != "Application voice" { t.Fatal("coupled voice selection changed", doc, err) } }