152 lines
5.4 KiB
Go
152 lines
5.4 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestSpeechNumbersPersistWithOneDecimal(t *testing.T) {
|
|
settings := DefaultSettings()
|
|
rate, pitch, gain := 61.29999999999999, 4.499999999999998, 4.999999999999999
|
|
settings.DefaultVoice = Voice{Rate: &rate, AveragePitch: &pitch, Gain: &gain}
|
|
settings.UppercaseVoice.AveragePitch = &pitch
|
|
settings.HyperlinkVoice.Rate = &rate
|
|
settings.SystemVoice.Gain = &gain
|
|
settings.EchoVoice = settings.DefaultVoice
|
|
path := filepath.Join(t.TempDir(), "settings.toml")
|
|
store, err := NewStore(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := store.Save(settings); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
loaded, err := store.Load()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for index, voice := range []Voice{loaded.DefaultVoice, loaded.UppercaseVoice, loaded.HyperlinkVoice, loaded.SystemVoice, loaded.EchoVoice} {
|
|
for field, value := range []*float64{voice.Rate, voice.AveragePitch, voice.Gain} {
|
|
if value != nil && *value != []float64{61.3, 4.5, 5}[field] {
|
|
t.Fatalf("voice %d field %d = %g", index, field, *value)
|
|
}
|
|
}
|
|
}
|
|
if loaded.UppercaseVoice.Rate != nil || loaded.HyperlinkVoice.Gain != nil || loaded.SystemVoice.AveragePitch != nil {
|
|
t.Fatal("rounding turned inheritance into explicit settings")
|
|
}
|
|
if *settings.DefaultVoice.AveragePitch != 4.499999999999998 {
|
|
t.Fatal("saving mutated caller's voice")
|
|
}
|
|
contents, err := os.ReadFile(path)
|
|
if err != nil || strings.Contains(string(contents), "999999") || !strings.Contains(string(contents), "average-pitch = 4.5") {
|
|
t.Fatalf("TOML does not contain rounded decimal values: %v", err)
|
|
}
|
|
if err := store.SaveForApplication("Example Editor", settings); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
contents, err = os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
document, _, err := decodeDocument(contents)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
profile := document.Profiles[document.Applications["Example Editor"]]
|
|
if profile.Speech.DefaultVoice != nil || profile.KeyEcho.EchoVoice != nil {
|
|
t.Fatal("floating-point noise created application voice overrides")
|
|
}
|
|
appGain := 3.299999999999999
|
|
settings.DefaultVoice.Gain = &appGain
|
|
if err := store.SaveForApplication("Example Editor", settings); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
application, found, err := store.LoadForApplication("Example Editor")
|
|
if err != nil || !found || *application.DefaultVoice.Gain != 3.3 {
|
|
t.Fatalf("application speech was not rounded: %+v, %v", application.DefaultVoice, err)
|
|
}
|
|
}
|
|
|
|
func TestVoiceTypesPersistAndInheritAcrossApplications(t *testing.T) {
|
|
settings := DefaultSettings()
|
|
rate, pitch, gain := 62.0, 8.0, 4.0
|
|
settings.DefaultVoice = Voice{Rate: &rate, AveragePitch: &pitch, Gain: &gain, Family: &VoiceFamily{Name: "Default person", Lang: "en"}}
|
|
upper, link, system := 8.5, 3.0, 2.0
|
|
settings.UppercaseVoice.AveragePitch = &upper
|
|
settings.HyperlinkVoice.Rate = &link
|
|
settings.SystemVoice.Gain = &system
|
|
settings.Reading.IndentationPresentationMode = "both"
|
|
store, err := NewStore(filepath.Join(t.TempDir(), "settings.toml"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := store.Save(settings); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
loaded, err := store.Load()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, kind := range []string{"uppercase", "hyperlink", "system"} {
|
|
if !reflect.DeepEqual(EffectiveVoice(loaded, kind), EffectiveVoice(settings, kind)) {
|
|
t.Fatalf("%s did not persist", kind)
|
|
}
|
|
}
|
|
if loaded.Reading.IndentationPresentationMode != "both" {
|
|
t.Fatal("indentation mode lost")
|
|
}
|
|
appSettings := loaded
|
|
appPitch := 5.0
|
|
appSettings.UppercaseVoice.AveragePitch = &appPitch
|
|
if err := store.SaveForApplication("example-app", appSettings); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
application, found, err := store.LoadForApplication("example-app")
|
|
if err != nil || !found || *EffectiveVoice(application, "uppercase").AveragePitch != 5 {
|
|
t.Fatalf("application voice = %+v, %v", application.UppercaseVoice, err)
|
|
}
|
|
global, err := store.Load()
|
|
if err != nil || *EffectiveVoice(global, "uppercase").AveragePitch != 8.5 {
|
|
t.Fatal("application voice changed global voice")
|
|
}
|
|
*store.Current().UppercaseVoice.AveragePitch = 0
|
|
if *store.Current().UppercaseVoice.AveragePitch != 8.5 {
|
|
t.Fatal("voice snapshots alias")
|
|
}
|
|
appSettings.UppercaseVoice = Voice{}
|
|
if err := store.SaveForApplication("example-app", appSettings); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
application, found, err = store.LoadForApplication("example-app")
|
|
if err != nil || !found || application.UppercaseVoice.AveragePitch != nil || *EffectiveVoice(application, "uppercase").AveragePitch != 9 {
|
|
t.Fatalf("application automatic pitch reset = %+v, %v", application.UppercaseVoice, err)
|
|
}
|
|
}
|
|
|
|
func TestAutomaticUppercasePitchAndExplicitOverride(t *testing.T) {
|
|
settings := DefaultSettings()
|
|
for _, tc := range []struct{ normal, upper float64 }{{3, 7}, {5, 7}, {7, 9}, {8, 9}, {9, 9}} {
|
|
settings.DefaultVoice.AveragePitch = &tc.normal
|
|
if got := *EffectiveVoice(settings, "uppercase").AveragePitch; got != tc.upper {
|
|
t.Fatalf("normal %g: uppercase %g, want %g", tc.normal, got, tc.upper)
|
|
}
|
|
}
|
|
chosen := 6.0
|
|
settings.UppercaseVoice.AveragePitch = &chosen
|
|
if *EffectiveVoice(settings, "uppercase").AveragePitch != chosen {
|
|
t.Fatal("explicit uppercase pitch ignored")
|
|
}
|
|
settings.Reading.IndentationPresentationMode = "invalid"
|
|
if Validate(settings) == nil {
|
|
t.Fatal("invalid indentation mode accepted")
|
|
}
|
|
}
|