aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pipewire/pipewire_test.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-12-02 06:03:21 +0900
committerOphestra <cat@gensokyo.uk>2025-12-02 06:03:21 +0900
commitaf741f20a003d48863eb5e9f7cf71052abd4116d (patch)
tree0ec7e558e0fdfae6a772d37c3c87c721ca882e6b /internal/pipewire/pipewire_test.go
parent39c6716fb0081249908433403c4da6298a4d12d3 (diff)
internal/pipewire: implement client context
This consumes the entire sample, is validated to send identical messages and correctly handle received messages. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/pipewire/pipewire_test.go')
-rw-r--r--internal/pipewire/pipewire_test.go805
1 files changed, 805 insertions, 0 deletions
diff --git a/internal/pipewire/pipewire_test.go b/internal/pipewire/pipewire_test.go
index 63b5f4fb..950311d6 100644
--- a/internal/pipewire/pipewire_test.go
+++ b/internal/pipewire/pipewire_test.go
@@ -3,10 +3,815 @@ package pipewire_test
import (
_ "embed"
"encoding/binary"
+ "fmt"
+ "net"
+ "reflect"
+ . "syscall"
+ "testing"
+ "time"
"hakurei.app/internal/pipewire"
)
+func TestContext(t *testing.T) {
+ t.Parallel()
+
+ var (
+ // Underlying connection stub holding test data.
+ conn = stubUnixConn{samples: []stubUnixConnSample{
+ {SYS_SENDMSG, samplePWContainer00, MSG_DONTWAIT | MSG_NOSIGNAL, nil, 0},
+ {SYS_RECVMSG, samplePWContainer01, MSG_DONTWAIT | MSG_CMSG_CLOEXEC, nil, 0},
+ {SYS_RECVMSG, "", MSG_DONTWAIT | MSG_CMSG_CLOEXEC, nil, EAGAIN},
+ {SYS_SENDMSG, samplePWContainer03, MSG_DONTWAIT | MSG_NOSIGNAL, nil, 0},
+ {SYS_RECVMSG, samplePWContainer04, MSG_DONTWAIT | MSG_CMSG_CLOEXEC, nil, 0},
+ {SYS_RECVMSG, "", MSG_DONTWAIT | MSG_CMSG_CLOEXEC, nil, EAGAIN},
+ {SYS_SENDMSG, samplePWContainer06, MSG_DONTWAIT | MSG_NOSIGNAL, []int{20, 21}, 0},
+ {SYS_RECVMSG, samplePWContainer07, MSG_DONTWAIT | MSG_CMSG_CLOEXEC, nil, 0},
+ {SYS_RECVMSG, "", MSG_DONTWAIT | MSG_CMSG_CLOEXEC, nil, EAGAIN},
+ }}
+
+ // Context instance under testing.
+ ctx = pipewire.MustNew(&conn, pipewire.SPADict{
+ {Key: "remote.intention", Value: "manager"},
+ {Key: "application.name", Value: "pw-container"},
+ {Key: "application.process.binary", Value: "pw-container"},
+ {Key: "application.language", Value: "en_US.UTF-8"},
+ {Key: "application.process.id", Value: "1443"},
+ {Key: "application.process.user", Value: "alice"},
+ {Key: "application.process.host", Value: "nixos"},
+ {Key: "application.process.session-id", Value: "1"},
+ {Key: "window.x11.display", Value: ":0"},
+ {Key: "cpu.vm.name", Value: "qemu"},
+ {Key: "log.level", Value: "0"},
+ {Key: "cpu.max-align", Value: "32"},
+ {Key: "default.clock.rate", Value: "48000"},
+ {Key: "default.clock.quantum", Value: "1024"},
+ {Key: "default.clock.min-quantum", Value: "32"},
+ {Key: "default.clock.max-quantum", Value: "2048"},
+ {Key: "default.clock.quantum-limit", Value: "8192"},
+ {Key: "default.clock.quantum-floor", Value: "4"},
+ {Key: "default.video.width", Value: "640"},
+ {Key: "default.video.height", Value: "480"},
+ {Key: "default.video.rate.num", Value: "25"},
+ {Key: "default.video.rate.denom", Value: "1"},
+ {Key: "clock.power-of-two-quantum", Value: "true"},
+ {Key: "link.max-buffers", Value: "64"},
+ {Key: "mem.warn-mlock", Value: "false"},
+ {Key: "mem.allow-mlock", Value: "true"},
+ {Key: "settings.check-quantum", Value: "false"},
+ {Key: "settings.check-rate", Value: "false"},
+ {Key: "core.version", Value: "1.4.7"},
+ {Key: "core.name", Value: "pipewire-alice-1443"},
+ })
+ )
+
+ var registry *pipewire.Registry
+ const wantRegistryId = 2
+ if r, err := ctx.GetRegistry(); err != nil {
+ t.Fatalf("GetRegistry: error = %v", err)
+ } else {
+ if r.ID != wantRegistryId {
+ t.Fatalf("GetRegistry: ID = %d, want %d", r.ID, wantRegistryId)
+ }
+ registry = r
+ }
+ if err := ctx.GetCore().Sync(); err != nil {
+ t.Fatalf("Sync: error = %v", err)
+ }
+
+ wantCoreInfo0 := pipewire.CoreInfo{
+ ID: 0,
+ Cookie: -2069267610,
+ UserName: "alice",
+ HostName: "nixos",
+ Version: "1.4.7",
+ Name: "pipewire-0",
+ ChangeMask: pipewire.PW_CORE_CHANGE_MASK_PROPS,
+ Properties: &pipewire.SPADict{
+ {Key: "config.name", Value: "pipewire.conf"},
+ {Key: "application.name", Value: "pipewire"},
+ {Key: "application.process.binary", Value: "pipewire"},
+ {Key: "application.language", Value: "en_US.UTF-8"},
+ {Key: "application.process.id", Value: "1446"},
+ {Key: "application.process.user", Value: "alice"},
+ {Key: "application.process.host", Value: "nixos"},
+ {Key: "window.x11.display", Value: ":0"},
+ {Key: "cpu.vm.name", Value: "qemu"},
+ {Key: "link.max-buffers", Value: "16"},
+ {Key: "core.daemon", Value: "true"},
+ {Key: "core.name", Value: "pipewire-0"},
+ {Key: "default.clock.min-quantum", Value: "1024"},
+ {Key: "cpu.max-align", Value: "32"},
+ {Key: "default.clock.rate", Value: "48000"},
+ {Key: "default.clock.quantum", Value: "1024"},
+ {Key: "default.clock.max-quantum", Value: "2048"},
+ {Key: "default.clock.quantum-limit", Value: "8192"},
+ {Key: "default.clock.quantum-floor", Value: "4"},
+ {Key: "default.video.width", Value: "640"},
+ {Key: "default.video.height", Value: "480"},
+ {Key: "default.video.rate.num", Value: "25"},
+ {Key: "default.video.rate.denom", Value: "1"},
+ {Key: "log.level", Value: "2"},
+ {Key: "clock.power-of-two-quantum", Value: "true"},
+ {Key: "mem.warn-mlock", Value: "false"},
+ {Key: "mem.allow-mlock", Value: "true"},
+ {Key: "settings.check-quantum", Value: "false"},
+ {Key: "settings.check-rate", Value: "false"},
+ {Key: "object.id", Value: "0"},
+ {Key: "object.serial", Value: "0"}},
+ }
+
+ wantClient0 := pipewire.Client{
+ Info: &pipewire.ClientInfo{
+ ID: 34,
+ ChangeMask: pipewire.PW_CLIENT_CHANGE_MASK_PROPS,
+ Properties: &pipewire.SPADict{
+ {Key: "pipewire.protocol", Value: "protocol-native"},
+ {Key: "core.name", Value: "pipewire-alice-1443"},
+ {Key: "pipewire.sec.socket", Value: "pipewire-0-manager"},
+ {Key: "pipewire.sec.pid", Value: "1443"},
+ {Key: "pipewire.sec.uid", Value: "1000"},
+ {Key: "pipewire.sec.gid", Value: "100"},
+ {Key: "module.id", Value: "2"},
+ {Key: "object.id", Value: "34"},
+ {Key: "object.serial", Value: "34"},
+ {Key: "remote.intention", Value: "manager"},
+ {Key: "application.name", Value: "pw-container"},
+ {Key: "application.process.binary", Value: "pw-container"},
+ {Key: "application.language", Value: "en_US.UTF-8"},
+ {Key: "application.process.id", Value: "1443"},
+ {Key: "application.process.user", Value: "alice"},
+ {Key: "application.process.host", Value: "nixos"},
+ {Key: "application.process.session-id", Value: "1"},
+ {Key: "window.x11.display", Value: ":0"},
+ {Key: "cpu.vm.name", Value: "qemu"},
+ {Key: "log.level", Value: "0"},
+ {Key: "cpu.max-align", Value: "32"},
+ {Key: "default.clock.rate", Value: "48000"},
+ {Key: "default.clock.quantum", Value: "1024"},
+ {Key: "default.clock.min-quantum", Value: "32"},
+ {Key: "default.clock.max-quantum", Value: "2048"},
+ {Key: "default.clock.quantum-limit", Value: "8192"},
+ {Key: "default.clock.quantum-floor", Value: "4"},
+ {Key: "default.video.width", Value: "640"},
+ {Key: "default.video.height", Value: "480"},
+ {Key: "default.video.rate.num", Value: "25"},
+ {Key: "default.video.rate.denom", Value: "1"},
+ {Key: "clock.power-of-two-quantum", Value: "true"},
+ {Key: "link.max-buffers", Value: "64"},
+ {Key: "mem.warn-mlock", Value: "false"},
+ {Key: "mem.allow-mlock", Value: "true"},
+ {Key: "settings.check-quantum", Value: "false"},
+ {Key: "settings.check-rate", Value: "false"},
+ {Key: "core.version", Value: "1.4.7"},
+ {Key: "pipewire.access", Value: "unrestricted"},
+ },
+ },
+ Properties: pipewire.SPADict{
+ {Key: "object.serial", Value: "34"},
+ {Key: "module.id", Value: "2"},
+ {Key: "pipewire.protocol", Value: "protocol-native"},
+ {Key: "pipewire.sec.pid", Value: "1443"},
+ {Key: "pipewire.sec.uid", Value: "1000"},
+ {Key: "pipewire.sec.gid", Value: "100"},
+ {Key: "pipewire.sec.socket", Value: "pipewire-0-manager"},
+ },
+ }
+
+ wantRegistry0 := pipewire.Registry{
+ ID: wantRegistryId,
+ Objects: map[pipewire.Int]pipewire.RegistryGlobal{
+ pipewire.PW_ID_CORE: {
+ ID: pipewire.PW_ID_CORE,
+ Permissions: pipewire.PW_CORE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Core,
+ Version: pipewire.PW_VERSION_CORE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "0"},
+ {Key: "core.name", Value: "pipewire-0"},
+ },
+ },
+
+ 1: {
+ ID: 1,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "1"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-rt"},
+ },
+ },
+
+ 3: {
+ ID: 3,
+ Permissions: pipewire.PW_SECURITY_CONTEXT_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_SecurityContext,
+ Version: pipewire.PW_VERSION_SECURITY_CONTEXT,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "3"},
+ },
+ },
+
+ 2: {
+ ID: 2,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "2"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-protocol-native"},
+ },
+ },
+
+ 5: {
+ ID: 5,
+ Permissions: pipewire.PW_PROFILER_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Profiler,
+ Version: pipewire.PW_VERSION_PROFILER,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "5"},
+ },
+ },
+
+ 4: {
+ ID: 4,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "4"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-profiler"},
+ },
+ },
+
+ 6: {
+ ID: 6,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "6"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-metadata"},
+ },
+ },
+
+ 7: {
+ ID: 7,
+ Permissions: pipewire.PW_FACTORY_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Factory,
+ Version: pipewire.PW_VERSION_FACTORY,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "7"},
+ {Key: "module.id", Value: "6"},
+ {Key: "factory.name", Value: "metadata"},
+ {Key: "factory.type.name", Value: pipewire.PW_TYPE_INTERFACE_Metadata},
+ {Key: "factory.type.version", Value: "3"},
+ },
+ },
+
+ 8: {
+ ID: 8,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "8"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-spa-device-factory"},
+ },
+ },
+
+ 9: {
+ ID: 9,
+ Permissions: pipewire.PW_FACTORY_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Factory,
+ Version: pipewire.PW_VERSION_FACTORY,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "9"},
+ {Key: "module.id", Value: "8"},
+ {Key: "factory.name", Value: "spa-device-factory"},
+ {Key: "factory.type.name", Value: pipewire.PW_TYPE_INTERFACE_Device},
+ {Key: "factory.type.version", Value: "3"},
+ },
+ },
+
+ 10: {
+ ID: 10,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "10"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-spa-node-factory"},
+ },
+ },
+
+ 11: {
+ ID: 11,
+ Permissions: pipewire.PW_FACTORY_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Factory,
+ Version: pipewire.PW_VERSION_FACTORY,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "11"},
+ {Key: "module.id", Value: "10"},
+ {Key: "factory.name", Value: "spa-node-factory"},
+ {Key: "factory.type.name", Value: pipewire.PW_TYPE_INTERFACE_Node},
+ {Key: "factory.type.version", Value: "3"},
+ },
+ },
+
+ 12: {
+ ID: 12,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "12"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-client-node"},
+ },
+ },
+
+ 13: {
+ ID: 13,
+ Permissions: pipewire.PW_FACTORY_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Factory,
+ Version: pipewire.PW_VERSION_FACTORY,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "13"},
+ {Key: "module.id", Value: "12"},
+ {Key: "factory.name", Value: "client-node"},
+ {Key: "factory.type.name", Value: pipewire.PW_TYPE_INTERFACE_ClientNode},
+ {Key: "factory.type.version", Value: "6"},
+ },
+ },
+
+ 14: {
+ ID: 14,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "14"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-client-device"},
+ },
+ },
+
+ 15: {
+ ID: 15,
+ Permissions: pipewire.PW_FACTORY_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Factory,
+ Version: pipewire.PW_VERSION_FACTORY,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "15"},
+ {Key: "module.id", Value: "14"},
+ {Key: "factory.name", Value: "client-device"},
+ {Key: "factory.type.name", Value: "Spa:Pointer:Interface:Device"},
+ {Key: "factory.type.version", Value: "0"},
+ },
+ },
+
+ 16: {
+ ID: 16,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "16"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-portal"},
+ },
+ },
+
+ 17: {
+ ID: 17,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "17"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-access"},
+ },
+ },
+
+ 18: {
+ ID: 18,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "18"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-adapter"},
+ },
+ },
+
+ 19: {
+ ID: 19,
+ Permissions: pipewire.PW_FACTORY_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Factory,
+ Version: pipewire.PW_VERSION_FACTORY,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "19"},
+ {Key: "module.id", Value: "18"},
+ {Key: "factory.name", Value: "adapter"},
+ {Key: "factory.type.name", Value: pipewire.PW_TYPE_INTERFACE_Node},
+ {Key: "factory.type.version", Value: "3"},
+ },
+ },
+
+ 20: {
+ ID: 20,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "20"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-link-factory"},
+ },
+ },
+
+ 21: {
+ ID: 21,
+ Permissions: pipewire.PW_FACTORY_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Factory,
+ Version: pipewire.PW_VERSION_FACTORY,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "21"},
+ {Key: "module.id", Value: "20"},
+ {Key: "factory.name", Value: "link-factory"},
+ {Key: "factory.type.name", Value: pipewire.PW_TYPE_INTERFACE_Link},
+ {Key: "factory.type.version", Value: "3"},
+ },
+ },
+
+ 22: {
+ ID: 22,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "22"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-session-manager"},
+ },
+ },
+
+ 23: {
+ ID: 23,
+ Permissions: pipewire.PW_FACTORY_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Factory,
+ Version: pipewire.PW_VERSION_FACTORY,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "23"},
+ {Key: "module.id", Value: "22"},
+ {Key: "factory.name", Value: "client-endpoint"},
+ {Key: "factory.type.name", Value: "PipeWire:Interface:ClientEndpoint"},
+ {Key: "factory.type.version", Value: "0"},
+ },
+ },
+
+ 24: {
+ ID: 24,
+ Permissions: pipewire.PW_FACTORY_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Factory,
+ Version: pipewire.PW_VERSION_FACTORY,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "24"},
+ {Key: "module.id", Value: "22"},
+ {Key: "factory.name", Value: "client-session"},
+ {Key: "factory.type.name", Value: "PipeWire:Interface:ClientSession"},
+ {Key: "factory.type.version", Value: "0"},
+ },
+ },
+
+ 25: {
+ ID: 25,
+ Permissions: pipewire.PW_FACTORY_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Factory,
+ Version: pipewire.PW_VERSION_FACTORY,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "25"},
+ {Key: "module.id", Value: "22"},
+ {Key: "factory.name", Value: "session"},
+ {Key: "factory.type.name", Value: "PipeWire:Interface:Session"},
+ {Key: "factory.type.version", Value: "0"},
+ },
+ },
+
+ 26: {
+ ID: 26,
+ Permissions: pipewire.PW_FACTORY_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Factory,
+ Version: pipewire.PW_VERSION_FACTORY,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "26"},
+ {Key: "module.id", Value: "22"},
+ {Key: "factory.name", Value: "endpoint"},
+ {Key: "factory.type.name", Value: "PipeWire:Interface:Endpoint"},
+ {Key: "factory.type.version", Value: "0"},
+ },
+ },
+
+ 27: {
+ ID: 27,
+ Permissions: pipewire.PW_FACTORY_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Factory,
+ Version: pipewire.PW_VERSION_FACTORY,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "27"},
+ {Key: "module.id", Value: "22"},
+ {Key: "factory.name", Value: "endpoint-stream"},
+ {Key: "factory.type.name", Value: "PipeWire:Interface:EndpointStream"},
+ {Key: "factory.type.version", Value: "0"},
+ },
+ },
+
+ 28: {
+ ID: 28,
+ Permissions: pipewire.PW_FACTORY_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Factory,
+ Version: pipewire.PW_VERSION_FACTORY,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "28"},
+ {Key: "module.id", Value: "22"},
+ {Key: "factory.name", Value: "endpoint-link"},
+ {Key: "factory.type.name", Value: "PipeWire:Interface:EndpointLink"},
+ {Key: "factory.type.version", Value: "0"},
+ },
+ },
+
+ 29: {
+ ID: 29,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "29"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-x11-bell"},
+ },
+ },
+
+ 30: {
+ ID: 30,
+ Permissions: pipewire.PW_MODULE_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Module,
+ Version: pipewire.PW_VERSION_MODULE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "30"},
+ {Key: "module.name", Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-jackdbus-detect"},
+ },
+ },
+
+ 31: {
+ ID: 31,
+ Permissions: pipewire.PW_PERM_RWXM, // why is this not PW_NODE_PERM_MASK?
+ Type: pipewire.PW_TYPE_INTERFACE_Node,
+ Version: pipewire.PW_VERSION_NODE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "31"},
+ {Key: "factory.id", Value: "11"},
+ {Key: "priority.driver", Value: "200000"},
+ {Key: "node.name", Value: "Dummy-Driver"},
+ },
+ },
+
+ 32: {
+ ID: 32,
+ Permissions: pipewire.PW_PERM_RWXM, // why is this not PW_NODE_PERM_MASK?
+ Type: pipewire.PW_TYPE_INTERFACE_Node,
+ Version: pipewire.PW_VERSION_NODE,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "32"},
+ {Key: "factory.id", Value: "11"},
+ {Key: "priority.driver", Value: "190000"},
+ {Key: "node.name", Value: "Freewheel-Driver"},
+ },
+ },
+
+ 33: {
+ ID: 33,
+ Permissions: pipewire.PW_METADATA_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Metadata,
+ Version: pipewire.PW_VERSION_METADATA,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "33"},
+ {Key: "metadata.name", Value: "settings"},
+ },
+ },
+
+ 34: {
+ ID: 34,
+ Permissions: pipewire.PW_CLIENT_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Client,
+ Version: pipewire.PW_VERSION_CLIENT,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "34"},
+ {Key: "module.id", Value: "2"},
+ {Key: "pipewire.protocol", Value: "protocol-native"},
+ {Key: "pipewire.sec.pid", Value: "1443"},
+ {Key: "pipewire.sec.uid", Value: "1000"},
+ {Key: "pipewire.sec.gid", Value: "100"},
+ {Key: "pipewire.sec.socket", Value: "pipewire-0-manager"},
+ {Key: "pipewire.access", Value: "unrestricted"},
+ {Key: "application.name", Value: "pw-container"},
+ },
+ },
+
+ 35: {
+ ID: 35,
+ Permissions: pipewire.PW_CLIENT_PERM_MASK,
+ Type: pipewire.PW_TYPE_INTERFACE_Client,
+ Version: pipewire.PW_VERSION_CLIENT,
+ Properties: &pipewire.SPADict{
+ {Key: "object.serial", Value: "35"},
+ {Key: "module.id", Value: "2"},
+ {Key: "pipewire.protocol", Value: "protocol-native"},
+ {Key: "pipewire.sec.pid", Value: "1447"},
+ {Key: "pipewire.sec.uid", Value: "1000"},
+ {Key: "pipewire.sec.gid", Value: "100"},
+ {Key: "pipewire.sec.socket", Value: "pipewire-0-manager"},
+ {Key: "pipewire.access", Value: "unrestricted"},
+ {Key: "application.name", Value: "WirePlumber"},
+ },
+ },
+ },
+ }
+
+ if coreInfo := ctx.GetCore().Info; !reflect.DeepEqual(coreInfo, &wantCoreInfo0) {
+ t.Fatalf("New: CoreInfo = %s, want %s", mustMarshalJSON(coreInfo), mustMarshalJSON(&wantCoreInfo0))
+ }
+ if client := ctx.GetClient(); !reflect.DeepEqual(client, &wantClient0) {
+ t.Fatalf("New: Client = %s, want %s", mustMarshalJSON(client), mustMarshalJSON(&wantClient0))
+ }
+ if registry.ID != wantRegistry0.ID {
+ t.Fatalf("GetRegistry: ID = %d, want %d", registry.ID, wantRegistry0.ID)
+ }
+ if !reflect.DeepEqual(registry.Objects, wantRegistry0.Objects) {
+ t.Fatalf("GetRegistry: Objects = %s, want %s", mustMarshalJSON(registry.Objects), mustMarshalJSON(wantRegistry0.Objects))
+ }
+
+ var securityContext *pipewire.SecurityContext
+ const wantSecurityContextId = 3
+ if c, err := registry.GetSecurityContext(); err != nil {
+ t.Fatalf("GetSecurityContext: error = %v", err)
+ } else {
+ if c.ID != wantSecurityContextId {
+ t.Fatalf("GetSecurityContext: ID = %d, want %d", c.ID, wantSecurityContextId)
+ }
+ securityContext = c
+ }
+ if err := ctx.Roundtrip(); err != nil {
+ t.Fatalf("Roundtrip: error = %v", err)
+ }
+
+ // none of these should change
+ if coreInfo := ctx.GetCore().Info; !reflect.DeepEqual(coreInfo, &wantCoreInfo0) {
+ t.Fatalf("Roundtrip: CoreInfo = %s, want %s", mustMarshalJSON(coreInfo), mustMarshalJSON(&wantCoreInfo0))
+ }
+ if client := ctx.GetClient(); !reflect.DeepEqual(client, &wantClient0) {
+ t.Fatalf("Roundtrip: Client = %s, want %s", mustMarshalJSON(client), mustMarshalJSON(&wantClient0))
+ }
+ if registry.ID != wantRegistry0.ID {
+ t.Fatalf("Roundtrip: ID = %d, want %d", registry.ID, wantRegistry0.ID)
+ }
+ if !reflect.DeepEqual(registry.Objects, wantRegistry0.Objects) {
+ t.Fatalf("Roundtrip: Objects = %s, want %s", mustMarshalJSON(registry.Objects), mustMarshalJSON(wantRegistry0.Objects))
+ }
+
+ if err := securityContext.Create(21, 20, pipewire.SPADict{
+ {Key: "pipewire.sec.engine", Value: "org.flatpak"},
+ {Key: "pipewire.access", Value: "restricted"},
+ }); err != nil {
+ t.Fatalf("SecurityContext.Create: error = %v", err)
+ }
+ if err := ctx.GetCore().Sync(); err != nil {
+ t.Fatalf("Sync: error = %v", err)
+ }
+
+ // none of these should change
+ if coreInfo := ctx.GetCore().Info; !reflect.DeepEqual(coreInfo, &wantCoreInfo0) {
+ t.Fatalf("Roundtrip: CoreInfo = %s, want %s", mustMarshalJSON(coreInfo), mustMarshalJSON(&wantCoreInfo0))
+ }
+ if client := ctx.GetClient(); !reflect.DeepEqual(client, &wantClient0) {
+ t.Fatalf("Roundtrip: Client = %s, want %s", mustMarshalJSON(client), mustMarshalJSON(&wantClient0))
+ }
+ if registry.ID != wantRegistry0.ID {
+ t.Fatalf("Roundtrip: ID = %d, want %d", registry.ID, wantRegistry0.ID)
+ }
+ if !reflect.DeepEqual(registry.Objects, wantRegistry0.Objects) {
+ t.Fatalf("Roundtrip: Objects = %s, want %s", mustMarshalJSON(registry.Objects), mustMarshalJSON(wantRegistry0.Objects))
+ }
+
+ if err := ctx.Close(); err != nil {
+ t.Fatalf("Close: error = %v", err)
+ }
+}
+
+// stubUnixConnSample is sample data held by stubUnixConn.
+type stubUnixConnSample struct {
+ nr uintptr
+ iovec string
+ flags uintptr
+ files []int
+ errno Errno
+}
+
+// stubUnixConn implements [pipewire.Conn] and checks the behaviour of [pipewire.Context].
+type stubUnixConn struct {
+ samples []stubUnixConnSample
+ current int
+
+ deadline *time.Time
+}
+
+// checkDeadline checks whether deadline is set reasonably.
+func (conn *stubUnixConn) checkDeadline() error {
+ if conn.deadline == nil || conn.deadline.Before(time.Now()) {
+ return fmt.Errorf("invalid deadline %v", conn.deadline)
+ }
+ conn.deadline = nil
+ return nil
+}
+
+// nextSample returns the current sample and increments the counter.
+func (conn *stubUnixConn) nextSample(nr uintptr) (sample *stubUnixConnSample, wantOOB []byte, err error) {
+ sample = &conn.samples[conn.current]
+ conn.current++
+ if sample.nr != nr {
+ err = fmt.Errorf("unexpected syscall %d", SYS_SENDMSG)
+ return
+ }
+ if len(sample.files) > 0 {
+ wantOOB = UnixRights(sample.files...)
+ }
+
+ return
+}
+
+func (conn *stubUnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *net.UnixAddr, err error) {
+ if conn.samples[conn.current-1].nr == SYS_SENDMSG {
+ if err = conn.checkDeadline(); err != nil {
+ return
+ }
+ }
+
+ var (
+ sample *stubUnixConnSample
+ wantOOB []byte
+ )
+ sample, wantOOB, err = conn.nextSample(SYS_RECVMSG)
+ if err != nil {
+ return
+ }
+
+ if copy(b, sample.iovec) != len(sample.iovec) {
+ err = fmt.Errorf("insufficient iovec size %d, want at least %d", len(b), len(sample.iovec))
+ }
+ if copy(oob, wantOOB) != len(wantOOB) {
+ err = fmt.Errorf("insufficient oob size %d, want at least %d", len(oob), len(wantOOB))
+ }
+
+ if sample.errno != 0 && sample.errno != EAGAIN {
+ err = sample.errno
+ }
+ return len(sample.iovec), len(wantOOB), MSG_CMSG_CLOEXEC, nil, nil
+}
+
+func (conn *stubUnixConn) WriteMsgUnix(b, oob []byte, addr *net.UnixAddr) (n, oobn int, err error) {
+ if addr != nil {
+ err = fmt.Errorf("WriteMsgUnix called with non-nil addr: %#v", addr)
+ return
+ }
+ if err = conn.checkDeadline(); err != nil {
+ return
+ }
+
+ var (
+ sample *stubUnixConnSample
+ wantOOB []byte
+ )
+ sample, wantOOB, err = conn.nextSample(SYS_SENDMSG)
+ if err != nil {
+ return
+ }
+
+ if string(b) != sample.iovec {
+ err = fmt.Errorf("iovec: %#v, want %#v", b, []byte(sample.iovec))
+ return
+ }
+ if string(oob[:len(wantOOB)]) != string(wantOOB) {
+ err = fmt.Errorf("oob: %#v, want %#v", oob[:len(wantOOB)], wantOOB)
+ return
+ }
+ return len(sample.iovec), len(wantOOB), nil
+}
+
+func (conn *stubUnixConn) SetDeadline(t time.Time) error { conn.deadline = &t; return nil }
+
+func (conn *stubUnixConn) Close() error {
+ if conn.current != len(conn.samples) {
+ return fmt.Errorf("consumed %d samples, want %d", conn.current, len(conn.samples))
+ }
+ return nil
+}
+
var (
//go:embed testdata/pw-container-00-sendmsg
samplePWContainer00 string