internal/system: integrate PipeWire SecurityContext
All checks were successful
Test / Create distribution (push) Successful in 29s
Test / Sandbox (race detector) (push) Successful in 42s
Test / Sandbox (push) Successful in 43s
Test / Hakurei (push) Successful in 47s
Test / Hakurei (race detector) (push) Successful in 46s
Test / Hpkg (push) Successful in 43s
Test / Flake checks (push) Successful in 1m32s
All checks were successful
Test / Create distribution (push) Successful in 29s
Test / Sandbox (race detector) (push) Successful in 42s
Test / Sandbox (push) Successful in 43s
Test / Hakurei (push) Successful in 47s
Test / Hakurei (race detector) (push) Successful in 46s
Test / Hpkg (push) Successful in 43s
Test / Flake checks (push) Successful in 1m32s
Tests for this Op happens to be the best out of everything due to the robust infrastructure offered by internal/pipewire. This is now ready to use in internal/outcome for implementing #26. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
487
internal/system/pipewire_test.go
Normal file
487
internal/system/pipewire_test.go
Normal file
@@ -0,0 +1,487 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"hakurei.app/container/stub"
|
||||
"hakurei.app/internal/acl"
|
||||
"hakurei.app/internal/pipewire"
|
||||
)
|
||||
|
||||
func TestPipeWireOp(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
checkOpBehaviour(t, checkNoParallel, []opBehaviourTestCase{
|
||||
{"success", 0xbeef, 0xff, &pipewireOp{nil,
|
||||
m(path.Join(t.TempDir(), "pipewire")),
|
||||
}, []stub.Call{
|
||||
call("pipewireConnect", stub.ExpectArgs{}, func() *pipewire.Context {
|
||||
if ctx, err := pipewire.New(&stubPipeWireConn{sendmsg: []string{
|
||||
|
||||
/* roundtrip 0 */
|
||||
|
||||
string([]byte{
|
||||
// header: Core::Hello
|
||||
0, 0, 0, 0,
|
||||
0x18, 0, 0, 1,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// Struct
|
||||
0x10, 0, 0, 0,
|
||||
0xe, 0, 0, 0,
|
||||
// Int: version = 4
|
||||
4, 0, 0, 0,
|
||||
4, 0, 0, 0,
|
||||
4, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
// header: Client::UpdateProperties
|
||||
1, 0, 0, 0,
|
||||
0x50, 0, 0, 2,
|
||||
1, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// Struct: spa_dict
|
||||
0x48, 0, 0, 0,
|
||||
0xe, 0, 0, 0,
|
||||
// Struct: spa_dict_item
|
||||
0x40, 0, 0, 0,
|
||||
0xe, 0, 0, 0,
|
||||
// Int: n_items
|
||||
4, 0, 0, 0,
|
||||
4, 0, 0, 0,
|
||||
1, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// String: key = "remote.intention"
|
||||
0x11, 0, 0, 0,
|
||||
8, 0, 0, 0,
|
||||
0x72, 0x65, 0x6d, 0x6f,
|
||||
0x74, 0x65, 0x2e, 0x69,
|
||||
0x6e, 0x74, 0x65, 0x6e,
|
||||
0x74, 0x69, 0x6f, 0x6e,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// String: value = "manager"
|
||||
8, 0, 0, 0,
|
||||
8, 0, 0, 0,
|
||||
0x6d, 0x61, 0x6e, 0x61,
|
||||
0x67, 0x65, 0x72, 0,
|
||||
|
||||
// header: Core::GetRegistry
|
||||
0, 0, 0, 0,
|
||||
0x28, 0, 0, 5,
|
||||
2, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// Struct
|
||||
0x20, 0, 0, 0,
|
||||
0xe, 0, 0, 0,
|
||||
// Int: version = 3
|
||||
4, 0, 0, 0,
|
||||
4, 0, 0, 0,
|
||||
3, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// Int: new_id = 2
|
||||
4, 0, 0, 0,
|
||||
4, 0, 0, 0,
|
||||
2, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// header: Core::Sync
|
||||
0, 0, 0, 0,
|
||||
0x28, 0, 0, 2,
|
||||
3, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// Struct
|
||||
0x20, 0, 0, 0,
|
||||
0xe, 0, 0, 0,
|
||||
// Int: id = 0
|
||||
4, 0, 0, 0,
|
||||
4, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// Int: seq = 0x40000003
|
||||
4, 0, 0, 0,
|
||||
4, 0, 0, 0,
|
||||
3, 0, 0, 0x40,
|
||||
0, 0, 0, 0,
|
||||
}),
|
||||
|
||||
/* roundtrip 1 */
|
||||
|
||||
string([]byte{
|
||||
// header: Registry::Bind
|
||||
2, 0, 0, 0,
|
||||
0x68, 0, 0, 1,
|
||||
4, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// Struct
|
||||
0x60, 0, 0, 0,
|
||||
0xe, 0, 0, 0,
|
||||
// Int: id = 3
|
||||
4, 0, 0, 0,
|
||||
4, 0, 0, 0,
|
||||
3, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// String: type = "PipeWire:Interface:SecurityContext"
|
||||
0x23, 0, 0, 0,
|
||||
8, 0, 0, 0,
|
||||
0x50, 0x69, 0x70, 0x65,
|
||||
0x57, 0x69, 0x72, 0x65,
|
||||
0x3a, 0x49, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x66, 0x61,
|
||||
0x63, 0x65, 0x3a, 0x53,
|
||||
0x65, 0x63, 0x75, 0x72,
|
||||
0x69, 0x74, 0x79, 0x43,
|
||||
0x6f, 0x6e, 0x74, 0x65,
|
||||
0x78, 0x74, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// Int: version = 3
|
||||
4, 0, 0, 0,
|
||||
4, 0, 0, 0,
|
||||
3, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// Int: new_id = 3
|
||||
4, 0, 0, 0,
|
||||
4, 0, 0, 0,
|
||||
3, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
}),
|
||||
|
||||
/* roundtrip 2 */
|
||||
|
||||
string([]byte{
|
||||
// header: SecurityContext::Create
|
||||
3, 0, 0, 0,
|
||||
0xa8, 0, 0, 1,
|
||||
5, 0, 0, 0,
|
||||
2, 0, 0, 0,
|
||||
// Struct
|
||||
0xa0, 0, 0, 0,
|
||||
0xe, 0, 0, 0,
|
||||
// Fd: listen_fd = 1
|
||||
8, 0, 0, 0,
|
||||
0x12, 0, 0, 0,
|
||||
1, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// Fd: close_fd = 0
|
||||
8, 0, 0, 0,
|
||||
0x12, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// Struct: spa_dict
|
||||
0x78, 0, 0, 0,
|
||||
0xe, 0, 0, 0,
|
||||
// Int: n_items = 2
|
||||
4, 0, 0, 0,
|
||||
4, 0, 0, 0,
|
||||
2, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// String: key = "pipewire.sec.engine"
|
||||
0x14, 0, 0, 0,
|
||||
8, 0, 0, 0,
|
||||
0x70, 0x69, 0x70, 0x65,
|
||||
0x77, 0x69, 0x72, 0x65,
|
||||
0x2e, 0x73, 0x65, 0x63,
|
||||
0x2e, 0x65, 0x6e, 0x67,
|
||||
0x69, 0x6e, 0x65, 0,
|
||||
0, 0, 0, 0,
|
||||
// String: value = "app.hakurei"
|
||||
0xc, 0, 0, 0,
|
||||
8, 0, 0, 0,
|
||||
0x61, 0x70, 0x70, 0x2e,
|
||||
0x68, 0x61, 0x6b, 0x75,
|
||||
0x72, 0x65, 0x69, 0,
|
||||
0, 0, 0, 0,
|
||||
// String: key = "pipewire.access"
|
||||
0x10, 0, 0, 0,
|
||||
8, 0, 0, 0,
|
||||
0x70, 0x69, 0x70, 0x65,
|
||||
0x77, 0x69, 0x72, 0x65,
|
||||
0x2e, 0x61, 0x63, 0x63,
|
||||
0x65, 0x73, 0x73, 0,
|
||||
// String: value = "restricted"
|
||||
0xb, 0, 0, 0,
|
||||
8, 0, 0, 0,
|
||||
0x72, 0x65, 0x73, 0x74,
|
||||
0x72, 0x69, 0x63, 0x74,
|
||||
0x65, 0x64, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// header: Core::Sync
|
||||
0, 0, 0, 0,
|
||||
0x28, 0, 0, 2,
|
||||
6, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// Struct
|
||||
0x20, 0, 0, 0,
|
||||
0xe, 0, 0, 0,
|
||||
// Int: id = 0
|
||||
4, 0, 0, 0,
|
||||
4, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
// Int: seq = 0x40000006
|
||||
4, 0, 0, 0,
|
||||
4, 0, 0, 0,
|
||||
6, 0, 0, 0x40,
|
||||
0, 0, 0, 0,
|
||||
}),
|
||||
}, recvmsg: []*stubMessage{
|
||||
|
||||
/* roundtrip 0 */
|
||||
|
||||
{pipewire.PW_ID_CORE, nil, &pipewire.CoreInfo{
|
||||
ID: pipewire.PW_ID_CORE,
|
||||
Cookie: -2069267610,
|
||||
UserName: "alice",
|
||||
HostName: "nixos",
|
||||
Version: "1.4.7",
|
||||
Name: "pipewire-0",
|
||||
ChangeMask: pipewire.PW_CORE_CHANGE_MASK_PROPS,
|
||||
Properties: &pipewire.SPADict{
|
||||
{Key: pipewire.PW_KEY_CONFIG_NAME, Value: "pipewire.conf"},
|
||||
{Key: pipewire.PW_KEY_APP_NAME, Value: "pipewire"},
|
||||
{Key: pipewire.PW_KEY_APP_PROCESS_BINARY, Value: "pipewire"},
|
||||
{Key: pipewire.PW_KEY_APP_LANGUAGE, Value: "en_US.UTF-8"},
|
||||
{Key: pipewire.PW_KEY_APP_PROCESS_ID, Value: "1446"},
|
||||
{Key: pipewire.PW_KEY_APP_PROCESS_USER, Value: "alice"},
|
||||
{Key: pipewire.PW_KEY_APP_PROCESS_HOST, Value: "nixos"},
|
||||
{Key: pipewire.PW_KEY_CORE_DAEMON, Value: "true"},
|
||||
{Key: pipewire.PW_KEY_CORE_NAME, Value: "pipewire-0"},
|
||||
{Key: pipewire.PW_KEY_OBJECT_ID, Value: "0"},
|
||||
{Key: pipewire.PW_KEY_OBJECT_SERIAL, Value: "0"},
|
||||
},
|
||||
}},
|
||||
{pipewire.PW_ID_CORE, nil, &pipewire.CoreBoundProps{
|
||||
ID: pipewire.PW_ID_CLIENT,
|
||||
GlobalID: 34,
|
||||
Properties: &pipewire.SPADict{
|
||||
{Key: pipewire.PW_KEY_OBJECT_SERIAL, Value: "34"},
|
||||
{Key: pipewire.PW_KEY_MODULE_ID, Value: "2"},
|
||||
{Key: pipewire.PW_KEY_PROTOCOL, Value: "protocol-native"},
|
||||
{Key: pipewire.PW_KEY_SEC_PID, Value: "1443"},
|
||||
{Key: pipewire.PW_KEY_SEC_UID, Value: "1000"},
|
||||
{Key: pipewire.PW_KEY_SEC_GID, Value: "100"},
|
||||
{Key: pipewire.PW_KEY_SEC_SOCKET, Value: "pipewire-0-manager"},
|
||||
},
|
||||
}},
|
||||
{pipewire.PW_ID_CLIENT, nil, &pipewire.ClientInfo{
|
||||
ID: 34,
|
||||
ChangeMask: pipewire.PW_CLIENT_CHANGE_MASK_PROPS,
|
||||
Properties: &pipewire.SPADict{
|
||||
{Key: pipewire.PW_KEY_PROTOCOL, Value: "protocol-native"},
|
||||
{Key: pipewire.PW_KEY_CORE_NAME, Value: "pipewire-alice-1443"},
|
||||
{Key: pipewire.PW_KEY_SEC_SOCKET, Value: "pipewire-0-manager"},
|
||||
{Key: pipewire.PW_KEY_SEC_PID, Value: "1443"},
|
||||
{Key: pipewire.PW_KEY_SEC_UID, Value: "1000"},
|
||||
{Key: pipewire.PW_KEY_SEC_GID, Value: "100"},
|
||||
{Key: pipewire.PW_KEY_MODULE_ID, Value: "2"},
|
||||
{Key: pipewire.PW_KEY_OBJECT_ID, Value: "34"},
|
||||
{Key: pipewire.PW_KEY_OBJECT_SERIAL, Value: "34"},
|
||||
{Key: pipewire.PW_KEY_REMOTE_INTENTION, Value: "manager"},
|
||||
{Key: pipewire.PW_KEY_ACCESS, Value: "unrestricted"},
|
||||
},
|
||||
}},
|
||||
{pipewire.PW_ID_CORE, nil, &pipewire.CoreDone{
|
||||
ID: -1,
|
||||
Sequence: 0,
|
||||
}},
|
||||
{2, nil, &pipewire.RegistryGlobal{
|
||||
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: pipewire.PW_KEY_OBJECT_SERIAL, Value: "0"},
|
||||
{Key: pipewire.PW_KEY_CORE_NAME, Value: "pipewire-0"},
|
||||
},
|
||||
}},
|
||||
{2, nil, &pipewire.RegistryGlobal{
|
||||
ID: 1,
|
||||
Permissions: pipewire.PW_MODULE_PERM_MASK,
|
||||
Type: pipewire.PW_TYPE_INTERFACE_Module,
|
||||
Version: pipewire.PW_VERSION_MODULE,
|
||||
Properties: &pipewire.SPADict{
|
||||
{Key: pipewire.PW_KEY_OBJECT_SERIAL, Value: "1"},
|
||||
{Key: pipewire.PW_KEY_MODULE_NAME, Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-rt"},
|
||||
},
|
||||
}},
|
||||
{2, nil, &pipewire.RegistryGlobal{
|
||||
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: pipewire.PW_KEY_OBJECT_SERIAL, Value: "3"},
|
||||
},
|
||||
}},
|
||||
{2, nil, &pipewire.RegistryGlobal{
|
||||
ID: 2,
|
||||
Permissions: pipewire.PW_MODULE_PERM_MASK,
|
||||
Type: pipewire.PW_TYPE_INTERFACE_Module,
|
||||
Version: pipewire.PW_VERSION_MODULE,
|
||||
Properties: &pipewire.SPADict{
|
||||
{Key: pipewire.PW_KEY_OBJECT_SERIAL, Value: "2"},
|
||||
{Key: pipewire.PW_KEY_MODULE_NAME, Value: pipewire.PIPEWIRE_MODULE_PREFIX + "module-protocol-native"},
|
||||
},
|
||||
}},
|
||||
{pipewire.PW_ID_CORE, nil, &pipewire.CoreDone{
|
||||
ID: 0,
|
||||
Sequence: pipewire.CoreSyncSequenceOffset + 3,
|
||||
}},
|
||||
{2, nil, &pipewire.RegistryGlobal{
|
||||
ID: 4,
|
||||
Permissions: pipewire.PW_CLIENT_PERM_MASK,
|
||||
Type: pipewire.PW_TYPE_INTERFACE_Client,
|
||||
Version: pipewire.PW_VERSION_CLIENT,
|
||||
Properties: &pipewire.SPADict{
|
||||
{Key: pipewire.PW_KEY_OBJECT_SERIAL, Value: "4"},
|
||||
{Key: pipewire.PW_KEY_MODULE_ID, Value: "2"},
|
||||
{Key: pipewire.PW_KEY_PROTOCOL, Value: "protocol-native"},
|
||||
{Key: pipewire.PW_KEY_SEC_PID, Value: "1447"},
|
||||
{Key: pipewire.PW_KEY_SEC_UID, Value: "1000"},
|
||||
{Key: pipewire.PW_KEY_SEC_GID, Value: "100"},
|
||||
{Key: pipewire.PW_KEY_SEC_SOCKET, Value: "pipewire-0-manager"},
|
||||
{Key: pipewire.PW_KEY_ACCESS, Value: "unrestricted"},
|
||||
{Key: pipewire.PW_KEY_APP_NAME, Value: "WirePlumber"},
|
||||
},
|
||||
}},
|
||||
nil,
|
||||
|
||||
/* roundtrip 1 */
|
||||
|
||||
{pipewire.PW_ID_CORE, nil, &pipewire.CoreBoundProps{
|
||||
ID: 3,
|
||||
GlobalID: 3,
|
||||
Properties: &pipewire.SPADict{
|
||||
{Key: pipewire.PW_KEY_OBJECT_SERIAL, Value: "3"},
|
||||
},
|
||||
}},
|
||||
nil,
|
||||
|
||||
/* roundtrip 2 */
|
||||
|
||||
{pipewire.PW_ID_CORE, nil, &pipewire.CoreDone{
|
||||
ID: 0,
|
||||
Sequence: pipewire.CoreSyncSequenceOffset + 6,
|
||||
}},
|
||||
nil,
|
||||
}}, pipewire.SPADict{
|
||||
{Key: pipewire.PW_KEY_REMOTE_INTENTION, Value: "manager"},
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return ctx
|
||||
}
|
||||
}, nil),
|
||||
call("verbosef", stub.ExpectArgs{"pipewire pathname socket on %q", []any{ignoreValue{}}}, nil, nil),
|
||||
|
||||
call("chmod", stub.ExpectArgs{ignoreValue{}, os.FileMode(0)}, nil, nil),
|
||||
call("aclUpdate", stub.ExpectArgs{ignoreValue{}, 0xbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, nil),
|
||||
}, nil, []stub.Call{
|
||||
call("verbosef", stub.ExpectArgs{"hanging up pipewire socket on %q", []any{ignoreValue{}}}, nil, nil),
|
||||
}, nil},
|
||||
})
|
||||
|
||||
checkOpsBuilder(t, "PipeWire", []opsBuilderTestCase{
|
||||
{"sample", 0xcafe, func(_ *testing.T, sys *I) {
|
||||
sys.PipeWire(m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"))
|
||||
}, []Op{&pipewireOp{nil,
|
||||
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
||||
}}, stub.Expect{}},
|
||||
})
|
||||
|
||||
checkOpIs(t, []opIsTestCase{
|
||||
{"dst differs", &pipewireOp{nil,
|
||||
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7d/pipewire"),
|
||||
}, &pipewireOp{nil,
|
||||
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
||||
}, false},
|
||||
|
||||
{"equals", &pipewireOp{nil,
|
||||
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
||||
}, &pipewireOp{nil,
|
||||
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
||||
}, true},
|
||||
})
|
||||
|
||||
checkOpMeta(t, []opMetaTestCase{
|
||||
{"sample", &pipewireOp{nil,
|
||||
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
||||
}, Process, "/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire",
|
||||
`pipewire socket at "/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"`},
|
||||
})
|
||||
}
|
||||
|
||||
// stubMessage is a [pipewire.Message] prepared ahead of time.
|
||||
type stubMessage struct {
|
||||
// Proxy Id included in the [pipewire.Header].
|
||||
id pipewire.Int
|
||||
// Footer optionally appended after the message body.
|
||||
footer pipewire.KnownSize
|
||||
// Known-good message prepared ahead of time.
|
||||
m pipewire.Message
|
||||
}
|
||||
|
||||
// stubPipeWireConn implements [pipewire.Conn] and checks the behaviour of [pipewire.Context].
|
||||
type stubPipeWireConn struct {
|
||||
// Marshaled and sent for Recvmsg.
|
||||
recvmsg []*stubMessage
|
||||
// Current position in recvmsg.
|
||||
curRecvmsg int
|
||||
// Current server seq number.
|
||||
sequence pipewire.Int
|
||||
|
||||
// Compared against calls to Sendmsg.
|
||||
sendmsg []string
|
||||
// Current position in sendmsg.
|
||||
curSendmsg int
|
||||
}
|
||||
|
||||
// Recvmsg marshals and copies a stubMessage prepared ahead of time.
|
||||
func (conn *stubPipeWireConn) Recvmsg(p, _ []byte, _ int) (n, _, recvflags int, err error) {
|
||||
defer func() { conn.curRecvmsg++ }()
|
||||
recvflags = syscall.MSG_CMSG_CLOEXEC
|
||||
|
||||
if conn.recvmsg[conn.curRecvmsg] == nil {
|
||||
err = syscall.EAGAIN
|
||||
return
|
||||
}
|
||||
defer func() { conn.sequence++ }()
|
||||
|
||||
if data, marshalErr := (pipewire.MessageEncoder{Message: conn.recvmsg[conn.curRecvmsg].m}).AppendMessage(nil,
|
||||
conn.recvmsg[conn.curRecvmsg].id,
|
||||
conn.sequence,
|
||||
conn.recvmsg[conn.curRecvmsg].footer,
|
||||
); marshalErr != nil {
|
||||
panic(marshalErr)
|
||||
} else {
|
||||
n = copy(p, data)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Sendmsg checks a client message against a known-good sample.
|
||||
func (conn *stubPipeWireConn) Sendmsg(p, _ []byte, _ int) (n int, err error) {
|
||||
defer func() { conn.curSendmsg++ }()
|
||||
|
||||
n = len(p)
|
||||
if string(p) != conn.sendmsg[conn.curSendmsg] {
|
||||
err = fmt.Errorf("%#v, want %#v", p, []byte(conn.sendmsg[conn.curSendmsg]))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Close checks whether Recvmsg and Sendmsg has depleted all samples.
|
||||
func (conn *stubPipeWireConn) Close() error {
|
||||
if conn.curRecvmsg != len(conn.recvmsg) {
|
||||
return fmt.Errorf("consumed %d recvmsg samples, want %d", conn.curRecvmsg, len(conn.recvmsg))
|
||||
}
|
||||
|
||||
if conn.curSendmsg != len(conn.sendmsg) {
|
||||
return fmt.Errorf("consumed %d sendmsg samples, want %d", conn.curSendmsg, len(conn.sendmsg))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user