Files
fenrir/internal/speech/hardware_usb_native_test.go

88 lines
2.7 KiB
Go

// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
//go:build linux && cgo
package speech
import (
"encoding/hex"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
func TestNativeUSBUsesOptionalABIAndReleasesResources(t *testing.T) {
root := t.TempDir()
library := filepath.Join(root, "libusb-1.0.so.0")
if output, err := exec.Command("cc", "-shared", "-fPIC", "-Wall", "-Wextra", "-Werror", "-o", library, "testdata/usb_fixture.c").CombinedOutput(); err != nil {
t.Fatalf("build USB fixture: %v: %s", err, output)
}
for _, mode := range []string{"ok", "unconfigured", "missing", "multiple", "denied", "busy", "short", "timeout"} {
t.Run(mode, func(t *testing.T) {
log := filepath.Join(t.TempDir(), "usb.log")
cmd := exec.Command(os.Args[0], "-test.run=^TestNativeUSBFixtureProcess$")
cmd.Env = append(os.Environ(), "LD_LIBRARY_PATH="+root, "FENRIR_USB_FIXTURE=1", "USB_FIXTURE_MODE="+mode, "USB_FIXTURE_LOG="+log)
if output, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("fixture process: %v: %s", err, output)
}
data, err := os.ReadFile(log)
if err != nil {
t.Fatal(err)
}
text := string(data)
if !strings.HasPrefix(text, "init\nlist\n") || !strings.Contains(text, "free-list\n") || !strings.HasSuffix(text, "exit\n") {
t.Fatal("USB cleanup", text)
}
opened := mode != "missing" && mode != "multiple" && mode != "denied"
if strings.Contains(text, "\nclose\n") != opened {
t.Fatal("handle cleanup", text)
}
claimed := opened && mode != "busy"
if strings.Contains(text, "\nrelease\n") != claimed {
t.Fatal("interface cleanup", text)
}
if mode == "ok" || mode == "unconfigured" {
for _, want := range []string{"write-02:" + hex.EncodeToString([]byte("[:timeout 1]")), "write-02:" + hex.EncodeToString([]byte("[:ra 180]")), "write-02:" + hex.EncodeToString([]byte("hello\x0b")), "write-04:06"} {
if !strings.Contains(text, want+"\n") {
t.Fatal("missing transfer", want, text)
}
}
}
if strings.Contains(text, "set-configuration\n") != (mode == "unconfigured") {
t.Fatal("unexpected USB configuration change", text)
}
})
}
}
func TestNativeUSBFixtureProcess(t *testing.T) {
if os.Getenv("FENRIR_USB_FIXTURE") != "1" {
return
}
transport, err := Open_driver("dectalk-usb", Options{})
mode := os.Getenv("USB_FIXTURE_MODE")
if mode != "ok" && mode != "unconfigured" {
if err == nil {
transport.Close()
t.Fatal("USB failure accepted", mode)
}
return
}
if err != nil {
t.Fatal(err)
}
defer transport.Close()
if err := transport.Configure(Parameters{}); err != nil {
t.Fatal(err)
}
if err := transport.Say("hello"); err != nil {
t.Fatal(err)
}
if err := transport.Cancel(); err != nil {
t.Fatal(err)
}
}