package mixer import ( "testing" ) func TestParseSinkLine(t *testing.T) { tests := []struct { name string input string wantOK bool wantID int wantName string wantVol float64 wantDef bool }{ { name: "default sink with volume", input: "*51. USB Audio [vol: 1.00]", wantOK: true, wantID: 51, wantName: "USB Audio", wantVol: 1.00, wantDef: true, }, { name: "non-default sink with volume", input: "33. Built-in Audio Analog Stereo [vol: 0.65]", wantOK: true, wantID: 33, wantName: "Built-in Audio Analog Stereo", wantVol: 0.65, wantDef: false, }, { name: "default sink with muted tag", input: "*51. USB Audio [vol: 0.50 MUTED]", wantOK: true, wantID: 51, wantName: "USB Audio", wantVol: 0.50, wantDef: true, }, { name: "sink without volume field", input: "33. Built-in Audio Analog Stereo", wantOK: true, wantID: 33, wantName: "Built-in Audio Analog Stereo", wantVol: 0.0, wantDef: false, }, { name: "empty string", input: "", wantOK: false, }, { name: "no dot separator", input: "no dot here", wantOK: false, }, { name: "non-numeric ID", input: "abc. Some Device [vol: 0.80]", wantOK: false, }, { name: "zero volume", input: "1. Muted Device [vol: 0.00]", wantOK: true, wantID: 1, wantName: "Muted Device", wantVol: 0.0, wantDef: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sink, ok := parseSinkLine(tt.input) if ok != tt.wantOK { t.Fatalf("parseSinkLine(%q) ok = %v, want %v", tt.input, ok, tt.wantOK) } if !ok { return } if sink.ID != tt.wantID { t.Errorf("ID = %d, want %d", sink.ID, tt.wantID) } if sink.Name != tt.wantName { t.Errorf("Name = %q, want %q", sink.Name, tt.wantName) } if sink.Volume != tt.wantVol { t.Errorf("Volume = %f, want %f", sink.Volume, tt.wantVol) } if sink.IsDefault != tt.wantDef { t.Errorf("IsDefault = %v, want %v", sink.IsDefault, tt.wantDef) } }) } } func TestParseSinks(t *testing.T) { tests := []struct { name string input string wantCount int wantNames []string }{ { name: "two sinks", input: `Audio Sinks: *51. USB Audio [vol: 1.00] 33. Built-in Audio Analog Stereo [vol: 0.65] Sources: 52. Microphone [vol: 0.80]`, wantCount: 2, wantNames: []string{"USB Audio", "Built-in Audio Analog Stereo"}, }, { name: "no sinks section", input: `Audio Sources: 52. Microphone [vol: 0.80]`, wantCount: 0, }, { name: "sinks with tree characters", input: `Audio ├─Sinks: │ ├─*51. USB Audio [vol: 1.00] │ └─33. Built-in Audio Analog Stereo [vol: 0.65] └─Sources: └─52. Microphone [vol: 0.80]`, wantCount: 2, wantNames: []string{"USB Audio", "Built-in Audio Analog Stereo"}, }, { name: "empty input", input: ``, wantCount: 0, }, { name: "video section ignored", input: `Video Sinks: 100. HDMI Output [vol: 1.00] Audio Sinks: 51. USB Audio [vol: 1.00]`, wantCount: 1, wantNames: []string{"USB Audio"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sinks, err := parseSinks([]byte(tt.input)) if err != nil { t.Fatalf("parseSinks returned error: %v", err) } if len(sinks) != tt.wantCount { t.Fatalf("got %d sinks, want %d", len(sinks), tt.wantCount) } for i, name := range tt.wantNames { if sinks[i].Name != name { t.Errorf("sink[%d].Name = %q, want %q", i, sinks[i].Name, name) } } }) } }