fix(audio): NullSink-Verwurf-Zeile im CLI-Pfad sichtbar machen
NewSink bekommt einen optionalen Log-Writer (die CLI übergibt stderr);
zuvor verwarf der null-Sink bei erfolgreicher Synthese stumm — die
sichtbare Zeile kam nur aus dem ErrNoByteOutput-Zweig. Sink-Interface
unverändert; Aufrufstellen ab Task 03 müssen den Writer übergeben.
TestSayArgumentMitNullSink beweist die Zeile jetzt extern auf dem
CLI-Pfad, TestNewSink deckt das Factory-Wiring ab; Live-Beweis mit
espeak-ng dokumentiert ("audio: N Bytes verworfen (null sink)", Exit 0).
Dazu (Minor): "was" aus englishWords entfernt — widersprach dem
Mehrdeutigkeits-Kommentar und dem Report; neuer TestFall
"Was macht er?" → de.
This commit is contained in:
1 parent
8306c6b384
commit
ae9f3b680e
6 files changed
+31
-10
No files matched your search
+1
-1
@@ -106,7 +106,7 @@ func runSay(args []string, opts *sayOptions, stdin io.Reader, stderr io.Writer)
|
|||||||
return exitFailure
|
return exitFailure
|
||||||
}
|
}
|
||||||
|
|
||||||
sink, err := audio.NewSink(cfg.Audio.Sink)
|
sink, err := audio.NewSink(cfg.Audio.Sink, stderr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(stderr, "talk: %v\n", err)
|
fmt.Fprintf(stderr, "talk: %v\n", err)
|
||||||
return exitFailure
|
return exitFailure
|
||||||
|
|||||||
@@ -52,7 +52,9 @@ cat "`+wav+`"`)
|
|||||||
|
|
||||||
// TestSayArgumentMitNullSink: `talk "Hallo Welt"` läuft mit
|
// TestSayArgumentMitNullSink: `talk "Hallo Welt"` läuft mit
|
||||||
// TALK_AUDIO_SINK=null tonlos durch (Abnahmekriterium) — Synthese läuft
|
// TALK_AUDIO_SINK=null tonlos durch (Abnahmekriterium) — Synthese läuft
|
||||||
// (fake espeak wurde gerufen), die Wiedergabe wird verworfen.
|
// (fake espeak wurde gerufen), und die Wiedergabe wird mit sichtbarer
|
||||||
|
// Verwurf-Zeile auf stderr verworfen (extern beobachtbares Verhalten des
|
||||||
|
// null-Sinks auf dem CLI-Pfad).
|
||||||
func TestSayArgumentMitNullSink(t *testing.T) {
|
func TestSayArgumentMitNullSink(t *testing.T) {
|
||||||
seen := withFakeEspeak(t)
|
seen := withFakeEspeak(t)
|
||||||
setGetenv(t, map[string]string{"TALK_AUDIO_SINK": "null"})
|
setGetenv(t, map[string]string{"TALK_AUDIO_SINK": "null"})
|
||||||
@@ -65,6 +67,9 @@ func TestSayArgumentMitNullSink(t *testing.T) {
|
|||||||
if _, err := os.Stat(seen); err != nil {
|
if _, err := os.Stat(seen); err != nil {
|
||||||
t.Errorf("Synthese lief nicht (fake espeak nicht gerufen): %v", err)
|
t.Errorf("Synthese lief nicht (fake espeak nicht gerufen): %v", err)
|
||||||
}
|
}
|
||||||
|
if !strings.Contains(errBuf.String(), "verworfen (null sink)") {
|
||||||
|
t.Errorf("stderr = %q, will Verwurf-Zeile des null-Sinks (Synthese war erfolgreich)", errBuf.String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestSaySubcommandWieDefault: `talk say "Text"` verhält sich wie der
|
// TestSaySubcommandWieDefault: `talk say "Text"` verhält sich wie der
|
||||||
|
|||||||
@@ -24,21 +24,23 @@ type Sink interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewSink liefert den Sink zum konfigurierten Namen ("oto" oder "null").
|
// NewSink liefert den Sink zum konfigurierten Namen ("oto" oder "null").
|
||||||
// Unbekannte Namen sind ein klarer Fehler.
|
// log ist ein optionaler Log-Output — die CLI übergibt ihr stderr, über den
|
||||||
func NewSink(name string) (Sink, error) {
|
// der null-Sink seine Verwurf-Zeile schreibt (nil = still verwerfen); der
|
||||||
|
// oto-Sink braucht kein Log. Unbekannte Namen sind ein klarer Fehler.
|
||||||
|
func NewSink(name string, log io.Writer) (Sink, error) {
|
||||||
switch name {
|
switch name {
|
||||||
case NameOto:
|
case NameOto:
|
||||||
return OtoSink{}, nil
|
return OtoSink{}, nil
|
||||||
case NameNull:
|
case NameNull:
|
||||||
return NullSink{}, nil
|
return NullSink{W: log}, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unbekannter audio sink %q (erlaubt: %q, %q)", name, NameOto, NameNull)
|
return nil, fmt.Errorf("unbekannter audio sink %q (erlaubt: %q, %q)", name, NameOto, NameNull)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NullSink verwirft jede Ansage ohne Ton — für CI, Server und Tests. Wurde
|
// NullSink verwirft jede Ansage ohne Ton — für CI, Server und Tests. Wurde
|
||||||
// ein W gesetzt, loggt Play eine Zeile darüber (der CLI-Pfad hängt stderr
|
// ein W gesetzt (NewSink reicht dafür den Log-Output der CLI durch), loggt
|
||||||
// an, damit „läuft ohne Ton durch" auch sichtbar bleibt).
|
// Play eine Zeile darüber, damit „läuft ohne Ton durch" sichtbar bleibt.
|
||||||
type NullSink struct {
|
type NullSink struct {
|
||||||
W io.Writer // optionaler Log-Output; nil = still verwerfen
|
W io.Writer // optionaler Log-Output; nil = still verwerfen
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ func TestDecodeWAVFehler(t *testing.T) {
|
|||||||
|
|
||||||
func TestNewSink(t *testing.T) {
|
func TestNewSink(t *testing.T) {
|
||||||
for _, name := range []string{NameOto, NameNull} {
|
for _, name := range []string{NameOto, NameNull} {
|
||||||
s, err := NewSink(name)
|
s, err := NewSink(name, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewSink(%q): %v", name, err)
|
t.Fatalf("NewSink(%q): %v", name, err)
|
||||||
}
|
}
|
||||||
@@ -123,9 +123,22 @@ func TestNewSink(t *testing.T) {
|
|||||||
t.Errorf("NewSink(%q).Name() = %q", name, s.Name())
|
t.Errorf("NewSink(%q).Name() = %q", name, s.Name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, err := NewSink("mpv"); err == nil {
|
if _, err := NewSink("mpv", nil); err == nil {
|
||||||
t.Error("NewSink(\"mpv\") soll fehlschlagen")
|
t.Error("NewSink(\"mpv\") soll fehlschlagen")
|
||||||
}
|
}
|
||||||
|
t.Run("null-Sink loggt über den Factory-Writer", func(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
s, err := NewSink(NameNull, &buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewSink(%q): %v", NameNull, err)
|
||||||
|
}
|
||||||
|
if err := s.Play(context.Background(), []byte("audio")); err != nil {
|
||||||
|
t.Fatalf("Play: %v", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(buf.String(), "verworfen (null sink)") {
|
||||||
|
t.Errorf("Log = %q, will Verwurf-Zeile über den Factory-Writer", buf.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNullSinkVerwirftUndLoggt(t *testing.T) {
|
func TestNullSinkVerwirftUndLoggt(t *testing.T) {
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ var englishWords = map[string]struct{}{
|
|||||||
"finished": {}, "started": {}, "completed": {}, "error": {},
|
"finished": {}, "started": {}, "completed": {}, "error": {},
|
||||||
"warning": {}, "thanks": {}, "you": {}, "your": {},
|
"warning": {}, "thanks": {}, "you": {}, "your": {},
|
||||||
"we": {}, "it": {}, "this": {}, "that": {}, "these": {},
|
"we": {}, "it": {}, "this": {}, "that": {}, "these": {},
|
||||||
"those": {}, "was": {}, "were": {}, "are": {}, "be": {},
|
"those": {}, "were": {}, "are": {}, "be": {},
|
||||||
"been": {}, "has": {}, "have": {}, "had": {}, "can": {},
|
"been": {}, "has": {}, "have": {}, "had": {}, "can": {},
|
||||||
"will": {}, "would": {}, "should": {}, "could": {}, "just": {},
|
"will": {}, "would": {}, "should": {}, "could": {}, "just": {},
|
||||||
"only": {}, "still": {}, "already": {}, "now": {}, "today": {},
|
"only": {}, "still": {}, "already": {}, "now": {}, "today": {},
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ func TestDetect(t *testing.T) {
|
|||||||
{"nur whitespace", " \n\t ", "de"},
|
{"nur whitespace", " \n\t ", "de"},
|
||||||
{"nur interpunktion", "!!! ??? ...", "de"},
|
{"nur interpunktion", "!!! ??? ...", "de"},
|
||||||
{"deutsch satz", "Der Build wurde erfolgreich abgeschlossen und das Deployment ist fertig.", "de"},
|
{"deutsch satz", "Der Build wurde erfolgreich abgeschlossen und das Deployment ist fertig.", "de"},
|
||||||
|
{"deutsch frage mit was", "Was macht er?", "de"},
|
||||||
{"deutsch umlaute", "Größe früh Ärzteübung — schön daß es klappt", "de"},
|
{"deutsch umlaute", "Größe früh Ärzteübung — schön daß es klappt", "de"},
|
||||||
{"ß alleine", "weiß", "de"},
|
{"ß alleine", "weiß", "de"},
|
||||||
{"englisch satz", "The build has finished and the deployment is done.", "en"},
|
{"englisch satz", "The build has finished and the deployment is done.", "en"},
|
||||||
|
|||||||
Reference in new issue
Block a user