v0.29.0 21b3150

output_test.go RawHistoryFolder

1package factory
2
3import (
4	"context"
5	"errors"
6	"fmt"
7	"testing"
8
9	"github.com/bnema/dumber/internal/application/port"
10	"github.com/bnema/dumber/internal/infrastructure/audio/null"
11)
12
13// failingFactory is a factory that always returns an error
14type failingFactory struct {
15	err error
16}
17
18func (f *failingFactory) NewStream(_ context.Context, _ port.AudioStreamFormat) (port.AudioOutputStream, error) {
19	return nil, f.err
20}
21
22func TestSelector_PrimaryFails_FallsBackToFallback(t *testing.T) {
23	// Arrange
24	primaryErr := errors.New("primary backend unavailable")
25	primary := &failingFactory{err: primaryErr}
26	fallback := &null.Factory{}
27
28	selector := &Selector{
29		Primary:  primary,
30		Fallback: fallback,
31	}
32
33	format := port.AudioStreamFormat{
34		SampleRate:      48000,
35		ChannelCount:    2,
36		FramesPerBuffer: 512,
37	}
38	ctx := context.Background()
39
40	// Act
41	stream, err := selector.NewStream(ctx, format)
42
43	// Assert
44	if err != nil {
45		t.Fatalf("Expected no error when fallback succeeds, got: %v", err)
46	}
47	if stream == nil {
48		t.Fatal("Expected non-nil stream from fallback")
49	}
50
51	// Verify the stream works (it's the null stream)
52	samples := make([][]float32, format.ChannelCount)
53	for i := range samples {
54		samples[i] = make([]float32, format.FramesPerBuffer)
55	}
56
57	if err := stream.Write(samples); err != nil {
58		t.Errorf("Write failed: %v", err)
59	}
60	if err := stream.Close(); err != nil {
61		t.Errorf("Close failed: %v", err)
62	}
63}
64
65func TestSelector_PrimarySucceeds_UsesPrimary(t *testing.T) {
66	// Arrange
67	primary := &null.Factory{} // Using null as primary for simplicity
68	fallback := &failingFactory{err: errors.New("fallback should not be called")}
69
70	selector := &Selector{
71		Primary:  primary,
72		Fallback: fallback,
73	}
74
75	format := port.AudioStreamFormat{
76		SampleRate:      48000,
77		ChannelCount:    2,
78		FramesPerBuffer: 512,
79	}
80	ctx := context.Background()
81
82	// Act
83	stream, err := selector.NewStream(ctx, format)
84
85	// Assert
86	if err != nil {
87		t.Fatalf("Expected no error, got: %v", err)
88	}
89	if stream == nil {
90		t.Fatal("Expected non-nil stream from primary")
91	}
92
93	// Verify the stream works
94	samples := make([][]float32, format.ChannelCount)
95	for i := range samples {
96		samples[i] = make([]float32, format.FramesPerBuffer)
97	}
98
99	if err := stream.Write(samples); err != nil {
100		t.Errorf("Write failed: %v", err)
101	}
102	if err := stream.Close(); err != nil {
103		t.Errorf("Close failed: %v", err)
104	}
105}
106
107func TestSelector_BothFail_ReturnsError(t *testing.T) {
108	// Arrange
109	primaryErr := errors.New("primary backend unavailable")
110	fallbackErr := errors.New("fallback backend unavailable")
111
112	selector := &Selector{
113		Primary:  &failingFactory{err: primaryErr},
114		Fallback: &failingFactory{err: fallbackErr},
115	}
116
117	format := port.AudioStreamFormat{
118		SampleRate:      48000,
119		ChannelCount:    2,
120		FramesPerBuffer: 512,
121	}
122	ctx := context.Background()
123
124	// Act
125	stream, err := selector.NewStream(ctx, format)
126
127	// Assert
128	if err == nil {
129		t.Fatal("Expected error when both backends fail")
130	}
131	if stream != nil {
132		t.Error("Expected nil stream when both backends fail")
133	}
134}
135
136// TestNewAudioOutputFactory_ReturnsWorkingFactory verifies that
137// NewAudioOutputFactory wires PipeWire as primary and null as fallback without
138// requiring a real PipeWire session.
139func TestNewAudioOutputFactory_ReturnsWorkingFactory(t *testing.T) {
140	factory := NewAudioOutputFactory()
141	if factory == nil {
142		t.Fatal("Expected non-nil factory")
143	}
144
145	selector, ok := factory.(*Selector)
146	if !ok {
147		t.Fatalf("Expected *Selector, got %T", factory)
148	}
149	if selector.Primary == nil {
150		t.Fatal("Expected non-nil primary backend")
151	}
152	if selector.Fallback == nil {
153		t.Fatal("Expected non-nil fallback backend")
154	}
155	if got := fmt.Sprintf("%T", selector.Primary); got != "*pipewire.Factory" {
156		t.Fatalf("Expected PipeWire primary backend, got %s", got)
157	}
158	if got := fmt.Sprintf("%T", selector.Fallback); got != "*null.Factory" {
159		t.Fatalf("Expected null fallback backend, got %s", got)
160	}
161}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close