docs: rewrite README and add MIT licence
- commit
- 2a9d8168696f3c5517d0ba4ad36f944e8bb30ea4
- author
- brice · · 2026-04-11 06:16 UTC
- parent
- 8b5fe8b
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +195 -24
A LICENCE +21 -0
| @@ -0,0 +1,21 @@ | |||
| 1 | + | MIT License | |
| 2 | + | ||
| 3 | + | Copyright (c) 2026 bnema | |
| 4 | + | ||
| 5 | + | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | + | of this software and associated documentation files (the "Software"), to deal | |
| 7 | + | in the Software without restriction, including without limitation the rights | |
| 8 | + | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | + | copies of the Software, and to permit persons to whom the Software is | |
| 10 | + | furnished to do so, subject to the following conditions: | |
| 11 | + | ||
| 12 | + | The above copyright notice and this permission notice shall be included in all | |
| 13 | + | copies or substantial portions of the Software. | |
| 14 | + | ||
| 15 | + | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | + | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | + | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | + | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | + | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | + | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| 21 | + | SOFTWARE. | |
| @@ -0,0 +1,21 @@ | |||||
| 1 | + | MIT License | |||
| 2 | + | ||||
| 3 | + | Copyright (c) 2026 bnema | |||
| 4 | + | ||||
| 5 | + | Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| 6 | + | of this software and associated documentation files (the "Software"), to deal | |||
| 7 | + | in the Software without restriction, including without limitation the rights | |||
| 8 | + | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| 9 | + | copies of the Software, and to permit persons to whom the Software is | |||
| 10 | + | furnished to do so, subject to the following conditions: | |||
| 11 | + | ||||
| 12 | + | The above copyright notice and this permission notice shall be included in all | |||
| 13 | + | copies or substantial portions of the Software. | |||
| 14 | + | ||||
| 15 | + | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| 16 | + | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| 17 | + | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| 18 | + | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| 19 | + | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| 20 | + | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
| 21 | + | SOFTWARE. | |||
M README.md +174 -24
| @@ -1,48 +1,198 @@ | |||
| 1 | 1 | # purego-pipewire | |
| 2 | 2 | ||
| 3 | - | Pure Go bindings for PipeWire using [purego](https://github.com/ebitengine/purego). | |
| 3 | + | Go bindings for [PipeWire](https://pipewire.org/) via [purego](https://github.com/ebitengine/purego) instead of cgo. | |
| 4 | + | ||
| 5 | + | Linux only. `CGO_ENABLED=0`. | |
| 6 | + | ||
| 7 | + | > [!WARNING] | |
| 8 | + | > This is early stage. The generated binding layer is in place, but the handwritten API is still focused on runtime lifecycle and float32 playback. API may change. | |
| 4 | 9 | ||
| 5 | 10 | ## Architecture | |
| 6 | 11 | ||
| 7 | - | This package follows the same layer split as other purego-* projects: | |
| 12 | + | The binding layer is **generated** from the checked-in model in `gen/pipewire.json`. The generator produces: | |
| 8 | 13 | ||
| 9 | - | - `internal/capi` — generated raw function types and registration helpers | |
| 10 | - | - `internal/ports/{in,out}` — generated port interfaces | |
| 11 | - | - `internal/core` — handwritten lifecycle logic | |
| 12 | - | - `internal/loader` — `dlopen` for PipeWire and SPA libs | |
| 13 | - | - `pipewire/` — public Go-facing wrappers | |
| 14 | - | - `spa/` — public constants and helpers | |
| 14 | + | - **purego function bindings** for PipeWire init, loop, and stream entry points | |
| 15 | + | - **Outbound port interfaces** grouped by API surface (`InitAPI`, `LoopAPI`, `StreamOps`) | |
| 16 | + | - **A composite CAPI interface** that embeds the generated port groups | |
| 17 | + | - **Inbound port interfaces** for the handwritten core layer | |
| 18 | + | - **Public API wrappers** in `pipewire/` and SPA constants/helpers in `spa/` | |
| 15 | 19 | ||
| 16 | - | ## Regenerating bindings | |
| 20 | + | A small hand-written core implements runtime init/deinit, player lifecycle, stream setup, and PCM buffer handoff for playback. | |
| 21 | + | ||
| 22 | + | Mocks for public interfaces and outbound ports are generated by [Mockery v3](https://vektra.github.io/mockery/) and shipped in `pipewire/mocks/` and `internal/ports/out/mocks/`. | |
| 23 | + | ||
| 24 | + | ## Usage | |
| 25 | + | ||
| 26 | + | ### Runtime lifecycle | |
| 27 | + | ||
| 28 | + | ```go | |
| 29 | + | import "github.com/bnema/purego-pipewire/pipewire" | |
| 30 | + | ||
| 31 | + | rt, err := pipewire.Init() | |
| 32 | + | if err != nil { | |
| 33 | + | return err | |
| 34 | + | } | |
| 35 | + | defer rt.Deinit() | |
| 36 | + | ||
| 37 | + | loop, err := rt.NewMainLoop() | |
| 38 | + | if err != nil { | |
| 39 | + | return err | |
| 40 | + | } | |
| 41 | + | defer loop.Destroy() | |
| 42 | + | ||
| 43 | + | go loop.Run() | |
| 44 | + | ``` | |
| 45 | + | ||
| 46 | + | ### Player playback | |
| 47 | + | ||
| 48 | + | ```go | |
| 49 | + | import "github.com/bnema/purego-pipewire/pipewire" | |
| 50 | + | ||
| 51 | + | rt, err := pipewire.Init() | |
| 52 | + | if err != nil { | |
| 53 | + | return err | |
| 54 | + | } | |
| 55 | + | defer rt.Deinit() | |
| 56 | + | ||
| 57 | + | player, err := pipewire.NewPlayer(pipewire.PlayerConfig{ | |
| 58 | + | SampleRate: 48000, | |
| 59 | + | Channels: 2, | |
| 60 | + | FramesPerBuffer: 256, | |
| 61 | + | SampleFormat: pipewire.SampleFormatF32, | |
| 62 | + | UnderrunPolicy: pipewire.UnderrunFillSilence, | |
| 63 | + | }, pipewire.PlayerCallbacks{ | |
| 64 | + | Fill: func(buf *pipewire.PCMBuffer) (int, error) { | |
| 65 | + | for ch := range buf.Samples { | |
| 66 | + | for i := range buf.Samples[ch] { | |
| 67 | + | buf.Samples[ch][i] = 0 | |
| 68 | + | } | |
| 69 | + | } | |
| 70 | + | return buf.Frames, nil | |
| 71 | + | }, | |
| 72 | + | }) | |
| 73 | + | if err != nil { | |
| 74 | + | return err | |
| 75 | + | } | |
| 76 | + | defer player.Close() | |
| 77 | + | ||
| 78 | + | if err := player.Start(); err != nil { | |
| 79 | + | return err | |
| 80 | + | } | |
| 81 | + | ``` | |
| 82 | + | ||
| 83 | + | ### Testing with mocks | |
| 84 | + | ||
| 85 | + | The package ships pre-generated [Mockery v3](https://vektra.github.io/mockery/) mocks for the public interfaces in `github.com/bnema/purego-pipewire/pipewire/mocks`. | |
| 17 | 86 | ||
| 18 | - | Normal contributors should run: | |
| 87 | + | **Step 1: Accept the interface in your code** | |
| 19 | 88 | ||
| 20 | - | go generate ./... | |
| 89 | + | ```go | |
| 90 | + | package output | |
| 21 | 91 | ||
| 22 | - | This uses checked-in model files under `gen/` (e.g. `gen/pipewire.json`). | |
| 92 | + | import "github.com/bnema/purego-pipewire/pipewire" | |
| 23 | 93 | ||
| 24 | - | Mocks are generated by [mockery](https://github.com/vektra/mockery): | |
| 94 | + | type Service struct { | |
| 95 | + | player pipewire.Player | |
| 96 | + | } | |
| 25 | 97 | ||
| 26 | - | mockery | |
| 98 | + | func New(player pipewire.Player) *Service { | |
| 99 | + | return &Service{player: player} | |
| 100 | + | } | |
| 27 | 101 | ||
| 28 | - | ### Maintainer workflow | |
| 102 | + | func (s *Service) Start() error { | |
| 103 | + | return s.player.Start() | |
| 104 | + | } | |
| 105 | + | ``` | |
| 29 | 106 | ||
| 30 | - | Maintainers can refresh the model from local PipeWire and SPA headers with: | |
| 107 | + | **Step 2: Mock it in tests** | |
| 31 | 108 | ||
| 32 | - | ./scripts/sync-headers.sh | |
| 33 | - | go generate ./... | |
| 109 | + | ```go | |
| 110 | + | package output_test | |
| 34 | 111 | ||
| 35 | - | The sync script is currently a placeholder for the planned header-import workflow. | |
| 112 | + | import ( | |
| 113 | + | "testing" | |
| 114 | + | ||
| 115 | + | "github.com/bnema/purego-pipewire/pipewire/mocks" | |
| 116 | + | "github.com/stretchr/testify/require" | |
| 117 | + | ) | |
| 118 | + | ||
| 119 | + | func TestServiceStart(t *testing.T) { | |
| 120 | + | mockPlayer := mocks.NewMockPlayer(t) | |
| 121 | + | mockPlayer.EXPECT().Start().Return(nil) | |
| 122 | + | ||
| 123 | + | svc := output.New(mockPlayer) | |
| 124 | + | require.NoError(t, svc.Start()) | |
| 125 | + | } | |
| 126 | + | ``` | |
| 127 | + | ||
| 128 | + | **Available mocks** in `github.com/bnema/purego-pipewire/pipewire/mocks`: | |
| 129 | + | ||
| 130 | + | | Mock | Interface | Key methods | | |
| 131 | + | |---|---|---| | |
| 132 | + | | `MockRuntime` | `pipewire.Runtime` | `Init`, `Deinit`, `NewMainLoop` | | |
| 133 | + | | `MockMainLoop` | `pipewire.MainLoop` | `Run`, `Destroy` | | |
| 134 | + | | `MockPlayer` | `pipewire.Player` | `Start`, `Pause`, `Stop`, `Close`, `State` | | |
| 135 | + | ||
| 136 | + | All mocks support `.EXPECT()` for type-safe expectations and `.On()`/`.Return()` for classic testify style. | |
| 36 | 137 | ||
| 37 | 138 | ## Requirements | |
| 38 | 139 | ||
| 39 | 140 | - Go 1.26.1+ | |
| 40 | - | - Linux with PipeWire 0.3 runtime libraries | |
| 141 | + | - Linux with `libpipewire-0.3.so.0` | |
| 142 | + | - A running PipeWire daemon for integration tests and real playback | |
| 143 | + | - Override path: `PIPEWIRE_LIB_PATH=/custom/path/libpipewire-0.3.so.0` | |
| 144 | + | ||
| 145 | + | ## Building | |
| 146 | + | ||
| 147 | + | ```sh | |
| 148 | + | CGO_ENABLED=0 go build ./... | |
| 149 | + | CGO_ENABLED=0 go test ./... | |
| 150 | + | ``` | |
| 151 | + | ||
| 152 | + | Integration tests need a real PipeWire daemon: | |
| 153 | + | ||
| 154 | + | ```sh | |
| 155 | + | go test -tags integration ./integration ./pipewire -v | |
| 156 | + | ``` | |
| 157 | + | ||
| 158 | + | The player integration suite checks public lifecycle transitions and proves that a live PipeWire process callback invokes the user-supplied `Fill` callback. | |
| 159 | + | ||
| 160 | + | ## Regenerating bindings | |
| 161 | + | ||
| 162 | + | ```sh | |
| 163 | + | go generate ./... | |
| 164 | + | ``` | |
| 165 | + | ||
| 166 | + | Or manually: | |
| 167 | + | ||
| 168 | + | ```sh | |
| 169 | + | go run ./cmd/pipewiregen | |
| 170 | + | mockery | |
| 171 | + | ``` | |
| 172 | + | ||
| 173 | + | Maintainers refreshing the checked-in model can run: | |
| 174 | + | ||
| 175 | + | ```sh | |
| 176 | + | ./scripts/sync-headers.sh | |
| 177 | + | go generate ./... | |
| 178 | + | ``` | |
| 41 | 179 | ||
| 42 | - | ## Integration tests | |
| 180 | + | ## Project layout | |
| 43 | 181 | ||
| 44 | - | Tagged integration tests require a **real PipeWire runtime/daemon** and verify the public lifecycle API against a live runtime: | |
| 182 | + | ```text | |
| 183 | + | pipewire/ public API + mocks | |
| 184 | + | spa/ public PipeWire and SPA constants/helpers | |
| 185 | + | internal/ | |
| 186 | + | ports/in/ inbound port interfaces | |
| 187 | + | ports/out/ outbound port interfaces + mocks | |
| 188 | + | core/ runtime and playback logic | |
| 189 | + | capi/ purego bindings + bridge | |
| 190 | + | loader/ dlopen libpipewire-0.3.so.0 | |
| 191 | + | cmd/pipewiregen/ binding generator (parser, model, emitter, templates) | |
| 192 | + | gen/ checked-in model data | |
| 193 | + | integration/ integration tests (require a real PipeWire daemon) | |
| 194 | + | ``` | |
| 45 | 195 | ||
| 46 | - | go test -tags integration ./integration ./pipewire -v | |
| 196 | + | ## License | |
| 47 | 197 | ||
| 48 | - | The player integration suite exercises state transitions (Idle → Playing → Stopped → Closed) and proves that the real start path reaches the PipeWire process callback, invoking the user-supplied `Fill` callback under a live daemon. Deeper cleanup internals are primarily covered by unit tests that use mocked dependencies. | |
| 198 | + | MIT | |
| @@ -1,48 +1,198 @@ | |||||
| 1 | # purego-pipewire | 1 | # purego-pipewire | ||
| 2 | 2 | ||||
| 3 | - | Pure Go bindings for PipeWire using [purego](https://github.com/ebitengine/purego). | 3 | + | Go bindings for [PipeWire](https://pipewire.org/) via [purego](https://github.com/ebitengine/purego) instead of cgo. |
| 4 | + | ||||
| 5 | + | Linux only. `CGO_ENABLED=0`. | |||
| 6 | + | ||||
| 7 | + | > [!WARNING] | |||
| 8 | + | > This is early stage. The generated binding layer is in place, but the handwritten API is still focused on runtime lifecycle and float32 playback. API may change. | |||
| 4 | 9 | ||||
| 5 | ## Architecture | 10 | ## Architecture | ||
| 6 | 11 | ||||
| 7 | - | This package follows the same layer split as other purego-* projects: | 12 | + | The binding layer is **generated** from the checked-in model in `gen/pipewire.json`. The generator produces: |
| 8 | 13 | ||||
| 9 | - | - `internal/capi` — generated raw function types and registration helpers | 14 | + | - **purego function bindings** for PipeWire init, loop, and stream entry points |
| 10 | - | - `internal/ports/{in,out}` — generated port interfaces | 15 | + | - **Outbound port interfaces** grouped by API surface (`InitAPI`, `LoopAPI`, `StreamOps`) |
| 11 | - | - `internal/core` — handwritten lifecycle logic | 16 | + | - **A composite CAPI interface** that embeds the generated port groups |
| 12 | - | - `internal/loader` — `dlopen` for PipeWire and SPA libs | 17 | + | - **Inbound port interfaces** for the handwritten core layer |
| 13 | - | - `pipewire/` — public Go-facing wrappers | 18 | + | - **Public API wrappers** in `pipewire/` and SPA constants/helpers in `spa/` |
| 14 | - | - `spa/` — public constants and helpers | |||
| 15 | 19 | ||||
| 16 | - | ## Regenerating bindings | 20 | + | A small hand-written core implements runtime init/deinit, player lifecycle, stream setup, and PCM buffer handoff for playback. |
| 21 | + | ||||
| 22 | + | Mocks for public interfaces and outbound ports are generated by [Mockery v3](https://vektra.github.io/mockery/) and shipped in `pipewire/mocks/` and `internal/ports/out/mocks/`. | |||
| 23 | + | ||||
| 24 | + | ## Usage | |||
| 25 | + | ||||
| 26 | + | ### Runtime lifecycle | |||
| 27 | + | ||||
| 28 | + | ```go | |||
| 29 | + | import "github.com/bnema/purego-pipewire/pipewire" | |||
| 30 | + | ||||
| 31 | + | rt, err := pipewire.Init() | |||
| 32 | + | if err != nil { | |||
| 33 | + | return err | |||
| 34 | + | } | |||
| 35 | + | defer rt.Deinit() | |||
| 36 | + | ||||
| 37 | + | loop, err := rt.NewMainLoop() | |||
| 38 | + | if err != nil { | |||
| 39 | + | return err | |||
| 40 | + | } | |||
| 41 | + | defer loop.Destroy() | |||
| 42 | + | ||||
| 43 | + | go loop.Run() | |||
| 44 | + | ``` | |||
| 45 | + | ||||
| 46 | + | ### Player playback | |||
| 47 | + | ||||
| 48 | + | ```go | |||
| 49 | + | import "github.com/bnema/purego-pipewire/pipewire" | |||
| 50 | + | ||||
| 51 | + | rt, err := pipewire.Init() | |||
| 52 | + | if err != nil { | |||
| 53 | + | return err | |||
| 54 | + | } | |||
| 55 | + | defer rt.Deinit() | |||
| 56 | + | ||||
| 57 | + | player, err := pipewire.NewPlayer(pipewire.PlayerConfig{ | |||
| 58 | + | SampleRate: 48000, | |||
| 59 | + | Channels: 2, | |||
| 60 | + | FramesPerBuffer: 256, | |||
| 61 | + | SampleFormat: pipewire.SampleFormatF32, | |||
| 62 | + | UnderrunPolicy: pipewire.UnderrunFillSilence, | |||
| 63 | + | }, pipewire.PlayerCallbacks{ | |||
| 64 | + | Fill: func(buf *pipewire.PCMBuffer) (int, error) { | |||
| 65 | + | for ch := range buf.Samples { | |||
| 66 | + | for i := range buf.Samples[ch] { | |||
| 67 | + | buf.Samples[ch][i] = 0 | |||
| 68 | + | } | |||
| 69 | + | } | |||
| 70 | + | return buf.Frames, nil | |||
| 71 | + | }, | |||
| 72 | + | }) | |||
| 73 | + | if err != nil { | |||
| 74 | + | return err | |||
| 75 | + | } | |||
| 76 | + | defer player.Close() | |||
| 77 | + | ||||
| 78 | + | if err := player.Start(); err != nil { | |||
| 79 | + | return err | |||
| 80 | + | } | |||
| 81 | + | ``` | |||
| 82 | + | ||||
| 83 | + | ### Testing with mocks | |||
| 84 | + | ||||
| 85 | + | The package ships pre-generated [Mockery v3](https://vektra.github.io/mockery/) mocks for the public interfaces in `github.com/bnema/purego-pipewire/pipewire/mocks`. | |||
| 17 | 86 | ||||
| 18 | - | Normal contributors should run: | 87 | + | **Step 1: Accept the interface in your code** |
| 19 | 88 | ||||
| 20 | - | go generate ./... | 89 | + | ```go |
| 90 | + | package output | |||
| 21 | 91 | ||||
| 22 | - | This uses checked-in model files under `gen/` (e.g. `gen/pipewire.json`). | 92 | + | import "github.com/bnema/purego-pipewire/pipewire" |
| 23 | 93 | ||||
| 24 | - | Mocks are generated by [mockery](https://github.com/vektra/mockery): | 94 | + | type Service struct { |
| 95 | + | player pipewire.Player | |||
| 96 | + | } | |||
| 25 | 97 | ||||
| 26 | - | mockery | 98 | + | func New(player pipewire.Player) *Service { |
| 99 | + | return &Service{player: player} | |||
| 100 | + | } | |||
| 27 | 101 | ||||
| 28 | - | ### Maintainer workflow | 102 | + | func (s *Service) Start() error { |
| 103 | + | return s.player.Start() | |||
| 104 | + | } | |||
| 105 | + | ``` | |||
| 29 | 106 | ||||
| 30 | - | Maintainers can refresh the model from local PipeWire and SPA headers with: | 107 | + | **Step 2: Mock it in tests** |
| 31 | 108 | ||||
| 32 | - | ./scripts/sync-headers.sh | 109 | + | ```go |
| 33 | - | go generate ./... | 110 | + | package output_test |
| 34 | 111 | ||||
| 35 | - | The sync script is currently a placeholder for the planned header-import workflow. | 112 | + | import ( |
| 113 | + | "testing" | |||
| 114 | + | ||||
| 115 | + | "github.com/bnema/purego-pipewire/pipewire/mocks" | |||
| 116 | + | "github.com/stretchr/testify/require" | |||
| 117 | + | ) | |||
| 118 | + | ||||
| 119 | + | func TestServiceStart(t *testing.T) { | |||
| 120 | + | mockPlayer := mocks.NewMockPlayer(t) | |||
| 121 | + | mockPlayer.EXPECT().Start().Return(nil) | |||
| 122 | + | ||||
| 123 | + | svc := output.New(mockPlayer) | |||
| 124 | + | require.NoError(t, svc.Start()) | |||
| 125 | + | } | |||
| 126 | + | ``` | |||
| 127 | + | ||||
| 128 | + | **Available mocks** in `github.com/bnema/purego-pipewire/pipewire/mocks`: | |||
| 129 | + | ||||
| 130 | + | | Mock | Interface | Key methods | | |||
| 131 | + | |---|---|---| | |||
| 132 | + | | `MockRuntime` | `pipewire.Runtime` | `Init`, `Deinit`, `NewMainLoop` | | |||
| 133 | + | | `MockMainLoop` | `pipewire.MainLoop` | `Run`, `Destroy` | | |||
| 134 | + | | `MockPlayer` | `pipewire.Player` | `Start`, `Pause`, `Stop`, `Close`, `State` | | |||
| 135 | + | ||||
| 136 | + | All mocks support `.EXPECT()` for type-safe expectations and `.On()`/`.Return()` for classic testify style. | |||
| 36 | 137 | ||||
| 37 | ## Requirements | 138 | ## Requirements | ||
| 38 | 139 | ||||
| 39 | - Go 1.26.1+ | 140 | - Go 1.26.1+ | ||
| 40 | - | - Linux with PipeWire 0.3 runtime libraries | 141 | + | - Linux with `libpipewire-0.3.so.0` |
| 142 | + | - A running PipeWire daemon for integration tests and real playback | |||
| 143 | + | - Override path: `PIPEWIRE_LIB_PATH=/custom/path/libpipewire-0.3.so.0` | |||
| 144 | + | ||||
| 145 | + | ## Building | |||
| 146 | + | ||||
| 147 | + | ```sh | |||
| 148 | + | CGO_ENABLED=0 go build ./... | |||
| 149 | + | CGO_ENABLED=0 go test ./... | |||
| 150 | + | ``` | |||
| 151 | + | ||||
| 152 | + | Integration tests need a real PipeWire daemon: | |||
| 153 | + | ||||
| 154 | + | ```sh | |||
| 155 | + | go test -tags integration ./integration ./pipewire -v | |||
| 156 | + | ``` | |||
| 157 | + | ||||
| 158 | + | The player integration suite checks public lifecycle transitions and proves that a live PipeWire process callback invokes the user-supplied `Fill` callback. | |||
| 159 | + | ||||
| 160 | + | ## Regenerating bindings | |||
| 161 | + | ||||
| 162 | + | ```sh | |||
| 163 | + | go generate ./... | |||
| 164 | + | ``` | |||
| 165 | + | ||||
| 166 | + | Or manually: | |||
| 167 | + | ||||
| 168 | + | ```sh | |||
| 169 | + | go run ./cmd/pipewiregen | |||
| 170 | + | mockery | |||
| 171 | + | ``` | |||
| 172 | + | ||||
| 173 | + | Maintainers refreshing the checked-in model can run: | |||
| 174 | + | ||||
| 175 | + | ```sh | |||
| 176 | + | ./scripts/sync-headers.sh | |||
| 177 | + | go generate ./... | |||
| 178 | + | ``` | |||
| 41 | 179 | ||||
| 42 | - | ## Integration tests | 180 | + | ## Project layout |
| 43 | 181 | ||||
| 44 | - | Tagged integration tests require a **real PipeWire runtime/daemon** and verify the public lifecycle API against a live runtime: | 182 | + | ```text |
| 183 | + | pipewire/ public API + mocks | |||
| 184 | + | spa/ public PipeWire and SPA constants/helpers | |||
| 185 | + | internal/ | |||
| 186 | + | ports/in/ inbound port interfaces | |||
| 187 | + | ports/out/ outbound port interfaces + mocks | |||
| 188 | + | core/ runtime and playback logic | |||
| 189 | + | capi/ purego bindings + bridge | |||
| 190 | + | loader/ dlopen libpipewire-0.3.so.0 | |||
| 191 | + | cmd/pipewiregen/ binding generator (parser, model, emitter, templates) | |||
| 192 | + | gen/ checked-in model data | |||
| 193 | + | integration/ integration tests (require a real PipeWire daemon) | |||
| 194 | + | ``` | |||
| 45 | 195 | ||||
| 46 | - | go test -tags integration ./integration ./pipewire -v | 196 | + | ## License |
| 47 | 197 | ||||
| 48 | - | The player integration suite exercises state transitions (Idle → Playing → Stopped → Closed) and proves that the real start path reaches the PipeWire process callback, invoking the user-supplied `Fill` callback under a live daemon. Deeper cleanup internals are primarily covered by unit tests that use mocked dependencies. | 198 | + | MIT |