Files
cthulhu/internal/app/configured_speaker_native_test.go

91 lines
3.0 KiB
Go

//go:build linux && cgo
// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"context"
"fmt"
"io"
"log/slog"
"os"
"strings"
"syscall"
"testing"
"time"
"unsafe"
"git.stormux.org/storm/cthulhu/internal/config"
"git.stormux.org/storm/cthulhu/internal/preferences"
)
func test_hardware_switch(t *testing.T, speaker *configuredSpeaker, software config.Settings) {
t.Helper()
fd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0600)
if err != nil {
t.Fatal(err)
}
master := os.NewFile(uintptr(fd), "hardware-speech-fixture")
defer master.Close()
var unlocked int32
var number uint32
if _, _, code := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&unlocked))); code != 0 {
t.Fatal(code)
}
if _, _, code := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&number))); code != 0 {
t.Fatal(code)
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
hardware := software
hardware.SpeechServerFactory = "hardwarefactory"
hardware.SpeechOutputModule = "litetalk"
hardware.HardwareSpeechDevice = fmt.Sprintf("/dev/pts/%d", number)
hardware.HardwareSpeechBaudRate = 9600
rate, pitch, volume := 99.0, 9.0, 9.0
hardware.DefaultVoice = config.Voice{Rate: &rate, AveragePitch: &pitch, Gain: &volume}
if err := speaker.UpdateConfiguration(ctx, hardware); err != nil {
t.Fatal(err)
}
check := func(speak func(context.Context, string) error, text, want string) {
t.Helper()
if err := speak(ctx, text); err != nil {
t.Fatal(err)
}
master.SetReadDeadline(time.Now().Add(time.Second))
buffer := make([]byte, len(want))
if _, err := io.ReadFull(master, buffer); err != nil || string(buffer) != want {
t.Fatalf("serial=%q want=%q error=%v", buffer, want, err)
}
}
check(speaker.Speak, "Default", "\x019S\x0199P\x019VDefault\r")
check(speaker.SpeakCharacter, "A", "\x019S\x0199P\x019VA\r")
check(speaker.SpeakCharacter, "a", "\x019S\x0199P\x019Va\r")
worker := newPreferencesWorker(slog.New(slog.DiscardHandler), hardware, speaker)
values := preferences.FromSettings(hardware)
rateLow := 0.0
values.Rate = &rateLow
if response := worker.apply(ctx, preferences.Request{Values: values}); !response.Accepted {
t.Fatal(response.Message)
}
check(speaker.Speak, "Applied", "\x18\x010S\x0199P\x019VApplied\r")
values.HardwareSpeechDevice = "/dev/cthulhu-missing-serial-fixture"
if response := worker.apply(ctx, preferences.Request{Values: values}); response.Accepted {
t.Fatal("unavailable serial device accepted")
}
if worker.settings.HardwareSpeechDevice != hardware.HardwareSpeechDevice {
t.Fatal("failed Apply committed a missing device")
}
check(speaker.Speak, "Retained", "\x010S\x0199P\x019VRetained\r")
if err := speaker.UpdateConfiguration(ctx, software); err != nil {
t.Fatal(err)
}
if !strings.HasPrefix(worker.settings.HardwareSpeechDevice, "/dev/pts/") {
t.Fatal("unexpected fixture device")
}
}