Files
talk/.superpowers/sdd/plan/review-8306c6b..ae9f3b6.diff
T

146 lines
7.4 KiB
Diff

# Review package: 8306c6b..HEAD
## Commits
ae9f3b6 fix(audio): NullSink-Verwurf-Zeile im CLI-Pfad sichtbar machen
## Files changed
cmd/talk/say.go | 2 +-
cmd/talk/say_test.go | 7 ++++++-
internal/audio/audio.go | 12 +++++++-----
internal/audio/audio_test.go | 17 +++++++++++++++--
internal/langdetect/detect.go | 2 +-
internal/langdetect/detect_test.go | 1 +
6 files changed, 31 insertions(+), 10 deletions(-)
## Diff
cmd/talk/say.go --- Go
106 return exitFailure
107 }
108
109 sink, err := audio.NewSink(cfg.Audio.Sink, stderr)
110 if err != nil {
111 fmt.Fprintf(stderr, "talk: %v\n", err)
112 return exitFailure
cmd/talk/say_test.go --- 1/2 --- Go
52 52
53 // TestSayArgumentMitNullSink: `talk 53 // TestSayArgumentMitNullSink: `talk
.. "Hallo Welt"` läuft mit .. "Hallo Welt"` läuft mit
54 // TALK_AUDIO_SINK=null tonlos durch 54 // TALK_AUDIO_SINK=null tonlos durch
.. (Abnahmekriterium) — Synthese läuft .. (Abnahmekriterium) — Synthese läuft
55 // (fake espeak wurde gerufen), die 55 // (fake espeak wurde gerufen), und
.. Wiedergabe wird verworfen. .. die Wiedergabe wird mit sichtbarer
.. 56 // Verwurf-Zeile auf stderr verworfe
.. .. n (extern beobachtbares Verhalten de
.. .. s
.. 57 // null-Sinks auf dem CLI-Pfad).
56 func TestSayArgumentMitNullSink(t *t 58 func TestSayArgumentMitNullSink(t *t
.. esting.T) { .. esting.T) {
57 seen := withFakeEspeak(t) 59 seen := withFakeEspeak(t)
58 setGetenv(t, map[string]string{" 60 setGetenv(t, map[string]string{"
.. TALK_AUDIO_SINK": "null"}) .. TALK_AUDIO_SINK": "null"})
cmd/talk/say_test.go --- 2/2 --- Go
65 67 if _, err := os.Stat(seen); err != nil {
66 68 t.Errorf("Synthese lief nicht (fake espeak nicht gerufen): %v", err)
67 69 }
.. 70 if !strings.Contains(errBuf.String(), "verworfen (null sink)") {
.. 71 t.Errorf("stderr = %q, will Verwurf-Zeile des null-Sinks (Synthese war erfolgreich)", errBuf.String())
.. 72 }
68 73 }
69 74
70 75 // TestSaySubcommandWieDefault: `talk say "Text"` verhält sich wie der
internal/audio/audio.go --- Go
24 } 24 }
25 25
26 // NewSink liefert den Sink zum konf 26 // NewSink liefert den Sink zum konf
.. igurierten Namen ("oto" oder "null") .. igurierten Namen ("oto" oder "null")
.. . .. .
.. 27 // log ist ein optionaler Log-Output
.. .. — die CLI übergibt ihr stderr, über
.. .. den
.. 28 // der null-Sink seine Verwurf-Zeile
.. .. schreibt (nil = still verwerfen); d
.. .. er
27 // Unbekannte Namen sind ein klarer 29 // oto-Sink braucht kein Log. Unbeka
.. Fehler. .. nnte Namen sind ein klarer Fehler.
28 func NewSink(name string) (Sink, err 30 func NewSink(name string, log io.Wri
.. or) { .. ter) (Sink, error) {
29 switch name { 31 switch name {
30 case NameOto: 32 case NameOto:
31 return OtoSink{}, nil 33 return OtoSink{}, nil
32 case NameNull: 34 case NameNull:
33 return NullSink{}, nil 35 return NullSink{W: log}, nil
34 default: 36 default:
35 return nil, fmt.Errorf("unbe 37 return nil, fmt.Errorf("unbe
.. kannter audio sink %q (erlaubt: %q, .. kannter audio sink %q (erlaubt: %q,
.. %q)", name, NameOto, NameNull) .. %q)", name, NameOto, NameNull)
36 } 38 }
37 } 39 }
38 40
39 // NullSink verwirft jede Ansage ohn 41 // NullSink verwirft jede Ansage ohn
.. e Ton — für CI, Server und Tests. Wu .. e Ton — für CI, Server und Tests. Wu
.. rde .. rde
40 // ein W gesetzt, loggt Play eine Ze 42 // ein W gesetzt (NewSink reicht daf
.. ile darüber (der CLI-Pfad hängt stde .. ür den Log-Output der CLI durch), lo
.. rr .. ggt
41 // an, damit „läuft ohne Ton durch" 43 // Play eine Zeile darüber, damit „l
.. auch sichtbar bleibt). .. äuft ohne Ton durch" sichtbar bleibt
.. .. .
42 type NullSink struct { 44 type NullSink struct {
43 W io.Writer // optionaler Log-Ou 45 W io.Writer // optionaler Log-Ou
.. tput; nil = still verwerfen .. tput; nil = still verwerfen
44 } 46 }
internal/audio/audio_test.go --- Go
115 115
116 116 func TestNewSink(t *testing.T) {
117 117 for _, name := range []string{NameOto, NameNull} {
118 118 s, err := NewSink(name, nil)
119 119 if err != nil {
120 120 t.Fatalf("NewSink(%q): %v", name, err)
121 121 }
122 122 if s.Name() != name {
123 123 t.Errorf("NewSink(%q).Name() = %q", name, s.Name())
124 124 }
125 125 }
126 126 if _, err := NewSink("mpv", nil); err == nil {
127 127 t.Error("NewSink(\"mpv\") soll fehlschlagen")
128 128 }
... 129 t.Run("null-Sink loggt über den Factory-Writer", func(t *testing.T) {
... 130 var buf bytes.Buffer
... 131 s, err := NewSink(NameNull, &buf)
... 132 if err != nil {
... 133 t.Fatalf("NewSink(%q): %v", NameNull, err)
... 134 }
... 135 if err := s.Play(context.Background(), []byte("audio")); err != nil {
... 136 t.Fatalf("Play: %v", err)
... 137 }
... 138 if !strings.Contains(buf.String(), "verworfen (null sink)") {
... 139 t.Errorf("Log = %q, will Verwurf-Zeile über den Factory-Writer", buf.String())
... 140 }
... 141 })
129 142 }
130 143
131 144 func TestNullSinkVerwirftUndLoggt(t *testing.T) {
internal/langdetect/detect.go --- Go
68 "finished": {}, "started": {}, "completed": {}, "error": {},
69 "warning": {}, "thanks": {}, "you": {}, "your": {},
70 "we": {}, "it": {}, "this": {}, "that": {}, "these": {},
71 "those": {}, "was": {}, "were": {}, "are": {}, "be": {},
72 "been": {}, "has": {}, "have": {}, "had": {}, "can": {},
73 "will": {}, "would": {}, "should": {}, "could": {}, "just": {},
74 "only": {}, "still": {}, "already": {}, "now": {}, "today": {},
internal/langdetect/detect_test.go --- Go
15 15 {"nur whitespace", " \n\t ", "de"},
16 16 {"nur interpunktion", "!!! ??? ...", "de"},
17 17 {"deutsch satz", "Der Build wurde erfolgreich abgeschlossen und das Deployment ist fertig.", "de"},
.. 18 {"deutsch frage mit was", "Was macht er?", "de"},
18 19 {"deutsch umlaute", "Größe früh Ärzteübung — schön daß es klappt", "de"},
19 20 {"ß alleine", "weiß", "de"},
20 21 {"englisch satz", "The build has finished and the deployment is done.", "en"},