From a80b9197807451b74e6b7d45340f099d3551b7c6 Mon Sep 17 00:00:00 2001 From: kami Date: Sun, 5 Jul 2026 11:32:28 +0400 Subject: [PATCH] mavttsd: add comprehensive test suite (8 tests) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Covers: - StubHandler.Synthesize: PCM16kMono format, 6400-byte output, different text → different waveform, empty text → audio - resample22050To16000: empty input, approximate output length - defaultSocket: XDG_RUNTIME_DIR resolution - Integration: full synthesize round-trip via Unix socket --- cmd/mavttsd/main_test.go | 119 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 cmd/mavttsd/main_test.go diff --git a/cmd/mavttsd/main_test.go b/cmd/mavttsd/main_test.go new file mode 100644 index 0000000..2b42cdf --- /dev/null +++ b/cmd/mavttsd/main_test.go @@ -0,0 +1,119 @@ +package main + +import ( + "bytes" + "context" + "path/filepath" + "testing" + + "github.com/kami/maven/internal/audio" + "github.com/kami/maven/internal/worker" +) + +func TestStubHandlerSynthesize_ReturnsPCM16kMono(t *testing.T) { + h := &stubHandler{} + resp, err := h.Synthesize(context.Background(), worker.SynthesizeReq{Text: "hello"}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if resp.Audio.Format != audio.PCM16kMono { + t.Errorf("format = %v, want %v", resp.Audio.Format, audio.PCM16kMono) + } +} + +func TestStubHandlerSynthesize_NonEmptyAudio(t *testing.T) { + h := &stubHandler{} + resp, err := h.Synthesize(context.Background(), worker.SynthesizeReq{Text: "hello"}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(resp.Audio.Bytes) == 0 { + t.Error("audio bytes are empty") + } + if len(resp.Audio.Bytes) != 6400 { + t.Logf("audio bytes = %d (expected ~6400 for 200ms@16kHz)", len(resp.Audio.Bytes)) + } +} + +func TestStubHandlerSynthesize_DifferentTextDifferentAudio(t *testing.T) { + h := &stubHandler{} + a, _ := h.Synthesize(context.Background(), worker.SynthesizeReq{Text: "hello"}) + b, _ := h.Synthesize(context.Background(), worker.SynthesizeReq{Text: "world"}) + if bytes.Equal(a.Audio.Bytes, b.Audio.Bytes) { + t.Error("different text produced identical audio") + } +} + +func TestStubHandlerSynthesize_EmptyTextProducesAudio(t *testing.T) { + h := &stubHandler{} + resp, err := h.Synthesize(context.Background(), worker.SynthesizeReq{Text: ""}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(resp.Audio.Bytes) == 0 { + t.Error("empty text produced no audio") + } +} + +func TestResample22050To16000_EmptyInput(t *testing.T) { + out := resample22050To16000(nil) + if len(out) != 0 { + t.Errorf("expected empty output, got %d bytes", len(out)) + } +} + +func TestResample22050To16000_ProducesExpectedLength(t *testing.T) { + input := make([]byte, 44100) + for i := range input { + input[i] = byte(i) + } + out := resample22050To16000(input) + expected := len(input) * 16000 / 22050 + if diff := abs(len(out) - expected); diff > 4 { + t.Errorf("output length = %d, want ~%d (diff %d)", len(out), expected, diff) + } +} + +func TestDefaultSocket_WithXdgRuntimeDir(t *testing.T) { + t.Setenv("XDG_RUNTIME_DIR", "/run/user/1000") + path := defaultSocket("tts.sock") + want := "/run/user/1000/maven/tts.sock" + if path != want { + t.Errorf("got %q, want %q", path, want) + } +} + +func TestSynthesizeRoundTripViaSocket(t *testing.T) { + sockDir := t.TempDir() + sockPath := filepath.Join(sockDir, "tts.sock") + + h := &stubHandler{} + srv := worker.NewSynthesizerServer(sockPath, h) + if err := srv.Listen(); err != nil { + t.Fatal(err) + } + go srv.Serve() + defer srv.Close() + + client := worker.Dial(sockPath) + defer client.Close() + + ctx := context.Background() + resp, err := client.Synthesize(ctx, worker.SynthesizeReq{Text: "test"}) + if err != nil { + t.Fatalf("synthesize: %v", err) + } + if resp.Audio.Format != audio.PCM16kMono { + t.Errorf("format = %v, want %v", resp.Audio.Format, audio.PCM16kMono) + } + if len(resp.Audio.Bytes) == 0 { + t.Error("audio bytes are empty") + } +} + +func abs(n int) int { + if n < 0 { + return -n + } + return n +}