210 lines
7.4 KiB
Go
210 lines
7.4 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package preferences
|
|
|
|
import (
|
|
"math"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/config"
|
|
)
|
|
|
|
func TestSpeechValuesMergeRoundsNumbersAndPreservesInheritance(t *testing.T) {
|
|
settings := config.DefaultSettings()
|
|
values := FromSettings(settings)
|
|
rate, pitch, gain := 61.29999999999999, 4.499999999999998, 4.999999999999999
|
|
values.Rate, values.Pitch, values.Volume = &rate, &pitch, &gain
|
|
values.UppercaseVoice.AveragePitch = &pitch
|
|
values.HyperlinkVoice.Rate = &rate
|
|
values.SystemVoice.Gain = &gain
|
|
values.EchoVoice = config.Voice{Rate: &rate, AveragePitch: &pitch, Gain: &gain}
|
|
merged, err := values.Merge(settings)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if *merged.DefaultVoice.Rate != 61.3 || *merged.DefaultVoice.AveragePitch != 4.5 || *merged.DefaultVoice.Gain != 5 ||
|
|
*merged.UppercaseVoice.AveragePitch != 4.5 || *merged.HyperlinkVoice.Rate != 61.3 || *merged.SystemVoice.Gain != 5 ||
|
|
*merged.EchoVoice.Rate != 61.3 || *merged.EchoVoice.AveragePitch != 4.5 || *merged.EchoVoice.Gain != 5 {
|
|
t.Fatal("speech values retained floating-point noise")
|
|
}
|
|
if merged.UppercaseVoice.Rate != nil || merged.HyperlinkVoice.Gain != nil || merged.SystemVoice.AveragePitch != nil {
|
|
t.Fatal("rounding turned inheritance into explicit settings")
|
|
}
|
|
if pitch != 4.499999999999998 {
|
|
t.Fatal("merging mutated caller's values")
|
|
}
|
|
for _, invalid := range []float64{9.01, -0.01, math.NaN(), math.Inf(1)} {
|
|
values.Pitch = &invalid
|
|
if _, err := values.Merge(settings); err == nil {
|
|
t.Fatalf("rounding hid invalid pitch %g", invalid)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSoundValuesDefaultAndMerge(t *testing.T) {
|
|
settings := config.DefaultSettings()
|
|
values := FromSettings(settings)
|
|
if !values.EnableSound || !values.PlaySoundForRole || !values.PlaySoundForState ||
|
|
!values.PlaySoundForPositionInSet ||
|
|
values.ProgressBar.OutputMode != "beep" || values.SpatializeObjectSounds {
|
|
t.Fatalf("default sound checkboxes = %+v", values)
|
|
}
|
|
|
|
values.EnableSound = false
|
|
values.EnableBraille = true
|
|
values.EnableContractedBraille = true
|
|
values.BrailleContractionTable = "en-us-g2.ctb"
|
|
values.SoundVolume = 0.75
|
|
values.SoundSink = "pulse"
|
|
values.SoundTheme = "none"
|
|
values.PlaySoundForRole = false
|
|
values.PlaySoundForState = false
|
|
values.PlaySoundForPositionInSet = false
|
|
values.SpatializeObjectSounds = true
|
|
values.ProgressBar.OutputMode = "off"
|
|
values.ProgressBar.ReportBackground = true
|
|
values.RoleSoundPresentation = "speech_only"
|
|
|
|
merged, err := values.Merge(settings)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := FromSettings(merged); !reflect.DeepEqual(got, values) {
|
|
t.Fatalf("sound values did not round trip:\n got %+v\nwant %+v", got, values)
|
|
}
|
|
}
|
|
|
|
func TestReadingValuesRoundTrip(t *testing.T) {
|
|
settings := config.DefaultSettings()
|
|
values := FromSettings(settings)
|
|
if values.Reading.OnlySpeakDisplayedText || !values.Reading.SpeakBlankLines ||
|
|
!values.SpeakDescription || values.Reading.ReadFullRowInSpreadSheet ||
|
|
!values.EnableSpeech || !values.Reading.EnablePauseBreaks ||
|
|
values.Reading.SpeakNumbersAsDigits || values.Reading.SpeakMultiCaseStringsAsWords ||
|
|
values.Reading.EnableIndentation || values.Reading.PhoneticCharacterHints {
|
|
t.Fatalf("default reading values = %+v", values)
|
|
}
|
|
|
|
values.EnableSpeech = false
|
|
values.SayAllStyle = "sentence"
|
|
values.SpeechVerbosityLevel = 0
|
|
values.Reading.OnlySpeakDisplayedText = true
|
|
values.Reading.EnablePauseBreaks = false
|
|
values.Reading.SpeakNumbersAsDigits = true
|
|
values.Reading.SpeakMultiCaseStringsAsWords = true
|
|
values.Reading.EnableIndentation = true
|
|
values.Reading.PhoneticCharacterHints = true
|
|
values.Reading.SpeakBlankLines = false
|
|
values.Reading.EnableMnemonicSpeaking = true
|
|
values.Reading.EnablePositionSpeaking = true
|
|
values.Reading.EnableTutorialMessages = true
|
|
values.Reading.MessagesAreDetailed = false
|
|
values.Reading.UseColorNames = false
|
|
values.Reading.PresentTooltips = true
|
|
values.Reading.EnableMouseReview = true
|
|
values.Reading.SpeakMisspelledIndicator = false
|
|
values.Reading.SpeakContextBlockquote = false
|
|
values.Reading.SpeakContextList = false
|
|
values.Reading.SpeakContextTable = false
|
|
values.Reading.SpeakContextPanel = false
|
|
values.Reading.SpeakContextLandmark = false
|
|
values.Reading.SpeakContextNonLandmarkForm = false
|
|
values.Reading.SayAllContextBlockquote = false
|
|
values.Reading.SayAllContextList = false
|
|
values.Reading.SayAllContextTable = false
|
|
values.Reading.SayAllContextPanel = false
|
|
values.Reading.SayAllContextLandmark = false
|
|
values.Reading.SayAllContextNonLandmarkForm = false
|
|
values.Reading.ReadFullRowInGUITable = false
|
|
values.Reading.ReadFullRowInDocumentTable = false
|
|
values.Reading.ReadFullRowInSpreadSheet = true
|
|
values.SpeakDescription = false
|
|
|
|
merged, err := values.Merge(settings)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := FromSettings(merged); !reflect.DeepEqual(got, values) {
|
|
t.Fatalf("reading values did not round trip:\n got %+v\nwant %+v", got, values)
|
|
}
|
|
}
|
|
|
|
func TestSpeechValuesRoundTrip(t *testing.T) {
|
|
settings := config.DefaultSettings()
|
|
settings.SpeechHistoryLimit = 1000
|
|
family := &config.VoiceFamily{Name: "Generic Voice", Lang: "en", Dialect: "US", Variant: "male"}
|
|
settings.SpeechOutputModule = "espeak-ng"
|
|
settings.DefaultVoice.Family = family
|
|
|
|
values := FromSettings(settings)
|
|
if values.SpeechOutputModule != "espeak-ng" || values.SpeechVoiceFamily != family || values.SpeechHistoryLimit != 1000 {
|
|
t.Fatalf("speech values = %+v", values)
|
|
}
|
|
merged, err := values.Merge(settings)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if merged.SpeechOutputModule != "espeak-ng" || merged.DefaultVoice.Family != family || merged.SpeechHistoryLimit != 1000 {
|
|
t.Fatalf("merged speech values = %+v", merged)
|
|
}
|
|
}
|
|
|
|
func TestKeyboardValuesMergeAndOwnership(t *testing.T) {
|
|
settings := config.DefaultSettings()
|
|
values := FromSettings(settings)
|
|
values.KeyboardLayout = 2
|
|
values.CthulhuModifierKeys = []string{"Caps_Lock", "Insert"}
|
|
merged, err := values.Merge(settings)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if merged.KeyboardLayout != 2 || !reflect.DeepEqual(merged.CthulhuModifierKeys, values.CthulhuModifierKeys) {
|
|
t.Fatalf("keyboard settings were not merged: %+v", merged)
|
|
}
|
|
values.CthulhuModifierKeys[0] = "KP_Insert"
|
|
if merged.CthulhuModifierKeys[0] != "Caps_Lock" {
|
|
t.Fatal("merged modifiers alias the UI values")
|
|
}
|
|
values.CthulhuModifierKeys = []string{}
|
|
merged, err = values.Merge(settings)
|
|
if err != nil || len(merged.CthulhuModifierKeys) != 0 {
|
|
t.Fatalf("cannot disable all modifiers: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCapitalCheckboxesRoundTrip(t *testing.T) {
|
|
for mask := 0; mask < 8; mask++ {
|
|
settings := config.DefaultSettings()
|
|
values := FromSettings(settings)
|
|
values.RaisePitchForCapitals = mask&1 != 0
|
|
values.BeepForCapitals = mask&2 != 0
|
|
values.SayCapForCapitals = mask&4 != 0
|
|
merged, err := values.Merge(settings)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := FromSettings(merged); !reflect.DeepEqual(got, values) {
|
|
t.Fatalf("mask %d did not round trip", mask)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStructuralFocusValuesRoundTrip(t *testing.T) {
|
|
settings := config.DefaultSettings()
|
|
values := FromSettings(settings)
|
|
if values.StructuralNavigationTriggersFocusMode {
|
|
t.Fatal("automatic structural focus defaults on")
|
|
}
|
|
values.StructuralNavigationTriggersFocusMode = true
|
|
merged, err := values.Merge(settings)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !FromSettings(merged).StructuralNavigationTriggersFocusMode {
|
|
t.Fatal("preference lost during Apply")
|
|
}
|
|
}
|