diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-12-02 06:03:21 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-12-02 06:03:21 +0900 |
| commit | af741f20a003d48863eb5e9f7cf71052abd4116d (patch) | |
| tree | 0ec7e558e0fdfae6a772d37c3c87c721ca882e6b /internal/pipewire/pipewire_test.go | |
| parent | 39c6716fb0081249908433403c4da6298a4d12d3 (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.go | 805 |
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 |
