detached 9f72b60

Apply Task 1 review fixes

- Rename cmd/pipewiregen/internal/pipewiregen to normalize for package consistency
- Wrap validation errors in parser.go with 'validation failed:' context
- Add validation tests for empty library name, soname, group name, symbol name, and signature
- Align README.md Go version (1.23+) with go.mod (1.23)
commit
9f72b603c7644f8d698eebe24e147d9572660d0b
author
brice · · 2026-04-09 05:18 UTC
parent
5b5bc74
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
9 files · +112 -2
M README.md +1 -1
@@ -27,5 +27,5 @@ Mocks are generated by [mockery](https://github.com/vektra/mockery):
2727
2828 ## Requirements
2929
30-- Go 1.24+
30+- Go 1.23+
3131 - Linux with PipeWire 0.3 runtime libraries
@@ -27,5 +27,5 @@ Mocks are generated by [mockery](https://github.com/vektra/mockery):
27 27
28 ## Requirements28 ## Requirements
29 29
30-- Go 1.24+30+- Go 1.23+
31 - Linux with PipeWire 0.3 runtime libraries31 - Linux with PipeWire 0.3 runtime libraries
R cmd/pipewiregen/internal/pipewiregen/normalize.go → cmd/pipewiregen/internal/normalize/normalize.go +0 -0 renamed, 100% similar

renamed, 100% similar

M cmd/pipewiregen/internal/parser/parser.go +1 -1
@@ -19,7 +19,7 @@ func Load(path string) (*model.Model, error) {
1919 return nil, fmt.Errorf("failed to unmarshal JSON from %s: %w", path, err)
2020 }
2121 if err := validate(&m); err != nil {
22- return nil, err
22+ return nil, fmt.Errorf("validation failed: %w", err)
2323 }
2424 return &m, nil
2525 }
@@ -19,7 +19,7 @@ func Load(path string) (*model.Model, error) {
19 return nil, fmt.Errorf("failed to unmarshal JSON from %s: %w", path, err)19 return nil, fmt.Errorf("failed to unmarshal JSON from %s: %w", path, err)
20 }20 }
21 if err := validate(&m); err != nil {21 if err := validate(&m); err != nil {
22- return nil, err22+ return nil, fmt.Errorf("validation failed: %w", err)
23 }23 }
24 return &m, nil24 return &m, nil
25 }25 }
M cmd/pipewiregen/internal/parser/parser_test.go +65 -0
@@ -47,3 +47,68 @@ func TestLoadSymbolWithUnknownLibraryReturnsError(t *testing.T) {
4747 t.Fatalf("error message should contain 'unknown library', got: %v", err)
4848 }
4949 }
50+
51+func TestLoadEmptyLibraryNameReturnsError(t *testing.T) {
52+ _, err := Load("testdata/empty_lib_name.json")
53+ if err == nil {
54+ t.Fatal("expected error for empty library name, got nil")
55+ }
56+ if !strings.Contains(err.Error(), "validation failed") {
57+ t.Fatalf("error message should contain 'validation failed', got: %v", err)
58+ }
59+ if !strings.Contains(err.Error(), "empty name") {
60+ t.Fatalf("error message should contain 'empty name', got: %v", err)
61+ }
62+}
63+
64+func TestLoadEmptyLibrarySONameReturnsError(t *testing.T) {
65+ _, err := Load("testdata/empty_lib_soname.json")
66+ if err == nil {
67+ t.Fatal("expected error for empty library soname, got nil")
68+ }
69+ if !strings.Contains(err.Error(), "validation failed") {
70+ t.Fatalf("error message should contain 'validation failed', got: %v", err)
71+ }
72+ if !strings.Contains(err.Error(), "empty soname") {
73+ t.Fatalf("error message should contain 'empty soname', got: %v", err)
74+ }
75+}
76+
77+func TestLoadEmptyGroupNameReturnsError(t *testing.T) {
78+ _, err := Load("testdata/empty_group_name.json")
79+ if err == nil {
80+ t.Fatal("expected error for empty group name, got nil")
81+ }
82+ if !strings.Contains(err.Error(), "validation failed") {
83+ t.Fatalf("error message should contain 'validation failed', got: %v", err)
84+ }
85+ if !strings.Contains(err.Error(), "empty name") {
86+ t.Fatalf("error message should contain 'empty name', got: %v", err)
87+ }
88+}
89+
90+func TestLoadEmptySymbolNameReturnsError(t *testing.T) {
91+ _, err := Load("testdata/empty_symbol_name.json")
92+ if err == nil {
93+ t.Fatal("expected error for empty symbol name, got nil")
94+ }
95+ if !strings.Contains(err.Error(), "validation failed") {
96+ t.Fatalf("error message should contain 'validation failed', got: %v", err)
97+ }
98+ if !strings.Contains(err.Error(), "empty name") {
99+ t.Fatalf("error message should contain 'empty name', got: %v", err)
100+ }
101+}
102+
103+func TestLoadEmptySymbolSignatureReturnsError(t *testing.T) {
104+ _, err := Load("testdata/empty_symbol_sig.json")
105+ if err == nil {
106+ t.Fatal("expected error for empty symbol signature, got nil")
107+ }
108+ if !strings.Contains(err.Error(), "validation failed") {
109+ t.Fatalf("error message should contain 'validation failed', got: %v", err)
110+ }
111+ if !strings.Contains(err.Error(), "empty signature") {
112+ t.Fatalf("error message should contain 'empty signature', got: %v", err)
113+ }
114+}
@@ -47,3 +47,68 @@ func TestLoadSymbolWithUnknownLibraryReturnsError(t *testing.T) {
47 t.Fatalf("error message should contain 'unknown library', got: %v", err)47 t.Fatalf("error message should contain 'unknown library', got: %v", err)
48 }48 }
49 }49 }
50+
51+func TestLoadEmptyLibraryNameReturnsError(t *testing.T) {
52+ _, err := Load("testdata/empty_lib_name.json")
53+ if err == nil {
54+ t.Fatal("expected error for empty library name, got nil")
55+ }
56+ if !strings.Contains(err.Error(), "validation failed") {
57+ t.Fatalf("error message should contain 'validation failed', got: %v", err)
58+ }
59+ if !strings.Contains(err.Error(), "empty name") {
60+ t.Fatalf("error message should contain 'empty name', got: %v", err)
61+ }
62+}
63+
64+func TestLoadEmptyLibrarySONameReturnsError(t *testing.T) {
65+ _, err := Load("testdata/empty_lib_soname.json")
66+ if err == nil {
67+ t.Fatal("expected error for empty library soname, got nil")
68+ }
69+ if !strings.Contains(err.Error(), "validation failed") {
70+ t.Fatalf("error message should contain 'validation failed', got: %v", err)
71+ }
72+ if !strings.Contains(err.Error(), "empty soname") {
73+ t.Fatalf("error message should contain 'empty soname', got: %v", err)
74+ }
75+}
76+
77+func TestLoadEmptyGroupNameReturnsError(t *testing.T) {
78+ _, err := Load("testdata/empty_group_name.json")
79+ if err == nil {
80+ t.Fatal("expected error for empty group name, got nil")
81+ }
82+ if !strings.Contains(err.Error(), "validation failed") {
83+ t.Fatalf("error message should contain 'validation failed', got: %v", err)
84+ }
85+ if !strings.Contains(err.Error(), "empty name") {
86+ t.Fatalf("error message should contain 'empty name', got: %v", err)
87+ }
88+}
89+
90+func TestLoadEmptySymbolNameReturnsError(t *testing.T) {
91+ _, err := Load("testdata/empty_symbol_name.json")
92+ if err == nil {
93+ t.Fatal("expected error for empty symbol name, got nil")
94+ }
95+ if !strings.Contains(err.Error(), "validation failed") {
96+ t.Fatalf("error message should contain 'validation failed', got: %v", err)
97+ }
98+ if !strings.Contains(err.Error(), "empty name") {
99+ t.Fatalf("error message should contain 'empty name', got: %v", err)
100+ }
101+}
102+
103+func TestLoadEmptySymbolSignatureReturnsError(t *testing.T) {
104+ _, err := Load("testdata/empty_symbol_sig.json")
105+ if err == nil {
106+ t.Fatal("expected error for empty symbol signature, got nil")
107+ }
108+ if !strings.Contains(err.Error(), "validation failed") {
109+ t.Fatalf("error message should contain 'validation failed', got: %v", err)
110+ }
111+ if !strings.Contains(err.Error(), "empty signature") {
112+ t.Fatalf("error message should contain 'empty signature', got: %v", err)
113+ }
114+}
A cmd/pipewiregen/internal/parser/testdata/empty_group_name.json +9 -0
@@ -0,0 +1,9 @@
1+{
2+ "libraries": [
3+ {"name": "pipewire", "soname": "libpipewire-0.3.so.0"}
4+ ],
5+ "groups": [
6+ {"name": "", "symbols": []}
7+ ],
8+ "symbols": []
9+}
@@ -0,0 +1,9 @@
1+{
2+ "libraries": [
3+ {"name": "pipewire", "soname": "libpipewire-0.3.so.0"}
4+ ],
5+ "groups": [
6+ {"name": "", "symbols": []}
7+ ],
8+ "symbols": []
9+}
A cmd/pipewiregen/internal/parser/testdata/empty_lib_name.json +7 -0
@@ -0,0 +1,7 @@
1+{
2+ "libraries": [
3+ {"name": "", "soname": "libpipewire-0.3.so.0"}
4+ ],
5+ "groups": [],
6+ "symbols": []
7+}
@@ -0,0 +1,7 @@
1+{
2+ "libraries": [
3+ {"name": "", "soname": "libpipewire-0.3.so.0"}
4+ ],
5+ "groups": [],
6+ "symbols": []
7+}
A cmd/pipewiregen/internal/parser/testdata/empty_lib_soname.json +7 -0
@@ -0,0 +1,7 @@
1+{
2+ "libraries": [
3+ {"name": "pipewire", "soname": ""}
4+ ],
5+ "groups": [],
6+ "symbols": []
7+}
@@ -0,0 +1,7 @@
1+{
2+ "libraries": [
3+ {"name": "pipewire", "soname": ""}
4+ ],
5+ "groups": [],
6+ "symbols": []
7+}
A cmd/pipewiregen/internal/parser/testdata/empty_symbol_name.json +11 -0
@@ -0,0 +1,11 @@
1+{
2+ "libraries": [
3+ {"name": "pipewire", "soname": "libpipewire-0.3.so.0"}
4+ ],
5+ "groups": [
6+ {"name": "init", "symbols": [""]}
7+ ],
8+ "symbols": [
9+ {"name": "", "library": "pipewire", "signature": "func()"}
10+ ]
11+}
@@ -0,0 +1,11 @@
1+{
2+ "libraries": [
3+ {"name": "pipewire", "soname": "libpipewire-0.3.so.0"}
4+ ],
5+ "groups": [
6+ {"name": "init", "symbols": [""]}
7+ ],
8+ "symbols": [
9+ {"name": "", "library": "pipewire", "signature": "func()"}
10+ ]
11+}
A cmd/pipewiregen/internal/parser/testdata/empty_symbol_sig.json +11 -0
@@ -0,0 +1,11 @@
1+{
2+ "libraries": [
3+ {"name": "pipewire", "soname": "libpipewire-0.3.so.0"}
4+ ],
5+ "groups": [
6+ {"name": "init", "symbols": ["pw_init"]}
7+ ],
8+ "symbols": [
9+ {"name": "pw_init", "library": "pipewire", "signature": ""}
10+ ]
11+}
@@ -0,0 +1,11 @@
1+{
2+ "libraries": [
3+ {"name": "pipewire", "soname": "libpipewire-0.3.so.0"}
4+ ],
5+ "groups": [
6+ {"name": "init", "symbols": ["pw_init"]}
7+ ],
8+ "symbols": [
9+ {"name": "pw_init", "library": "pipewire", "signature": ""}
10+ ]
11+}

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 1

Tags 8

↑↓ moveenter openesc close