forked from security/hakurei
These are mostly small formatting changes, with the biggest change being to UnexpectedEOFError where its kind is now described as part of the error type. Signed-off-by: Ophestra <cat@gensokyo.uk>
227 lines
6.2 KiB
Go
227 lines
6.2 KiB
Go
package pipewire_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding"
|
|
"encoding/gob"
|
|
"encoding/json"
|
|
"io"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"hakurei.app/internal/pipewire"
|
|
)
|
|
|
|
type encodingTestCases[V any, S interface {
|
|
encoding.BinaryMarshaler
|
|
encoding.BinaryUnmarshaler
|
|
|
|
*V
|
|
}] []struct {
|
|
// Uninterpreted name of subtest.
|
|
name string
|
|
// Encoded data.
|
|
wantData []byte
|
|
// Value corresponding to wantData.
|
|
value V
|
|
// Expected decoding error. Skips encoding check if non-nil.
|
|
wantErr error
|
|
}
|
|
|
|
// run runs all test cases as subtests of [testing.T].
|
|
func (testCases encodingTestCases[V, S]) run(t *testing.T) {
|
|
t.Helper()
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("decode", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var value V
|
|
if err := S(&value).UnmarshalBinary(tc.wantData); err != nil {
|
|
t.Fatalf("UnmarshalBinary: error = %v", err)
|
|
}
|
|
if !reflect.DeepEqual(&value, &tc.value) {
|
|
t.Fatalf("UnmarshalBinary:\n%s\nwant\n%s", mustMarshalJSON(value), mustMarshalJSON(tc.value))
|
|
}
|
|
})
|
|
|
|
t.Run("encode", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if gotData, err := S(&tc.value).MarshalBinary(); err != nil {
|
|
t.Fatalf("MarshalBinary: error = %v", err)
|
|
} else if string(gotData) != string(tc.wantData) {
|
|
t.Fatalf("MarshalBinary: %#v, want %#v", gotData, tc.wantData)
|
|
}
|
|
})
|
|
|
|
if s, ok := any(&tc.value).(pipewire.KnownSize); ok {
|
|
t.Run("size", func(t *testing.T) {
|
|
if got := int(s.Size()); got != len(tc.wantData) {
|
|
t.Errorf("Size: %d, want %d", got, len(tc.wantData))
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPODErrors(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
err error
|
|
want string
|
|
}{
|
|
{"UnsupportedTypeError", &pipewire.UnsupportedTypeError{
|
|
Type: reflect.TypeFor[any](),
|
|
}, "unsupported type interface {}"},
|
|
|
|
{"UnsupportedSizeError", pipewire.UnsupportedSizeError(pipewire.SizeMax + 1), "size 16777216 out of range"},
|
|
|
|
{"InvalidUnmarshalError untyped nil", new(pipewire.InvalidUnmarshalError), "attempting to unmarshal to nil"},
|
|
{"InvalidUnmarshalError non-pointer", &pipewire.InvalidUnmarshalError{
|
|
Type: reflect.TypeFor[uintptr](),
|
|
}, "attempting to unmarshal to non-pointer type uintptr"},
|
|
{"InvalidUnmarshalError nil", &pipewire.InvalidUnmarshalError{
|
|
Type: reflect.TypeFor[*uintptr](),
|
|
}, "attempting to unmarshal to nil *uintptr"},
|
|
|
|
{"UnexpectedEOFError ErrEOFPrefix", pipewire.ErrEOFPrefix, "unexpected EOF decoding fixed-size POD prefix"},
|
|
{"UnexpectedEOFError ErrEOFData", pipewire.ErrEOFData, "unexpected EOF establishing POD data bounds"},
|
|
{"UnexpectedEOFError ErrEOFDataString", pipewire.ErrEOFDataString, "unexpected EOF establishing POD String bounds"},
|
|
{"UnexpectedEOFError invalid", pipewire.UnexpectedEOFError(0xbad), "unexpected EOF"},
|
|
|
|
{"UnmarshalSetError", &pipewire.UnmarshalSetError{
|
|
Type: reflect.TypeFor[*uintptr](),
|
|
}, "cannot set *uintptr"},
|
|
|
|
{"TrailingGarbageError short", make(pipewire.TrailingGarbageError, 1<<3-1), "got 7 bytes of trailing garbage"},
|
|
{"TrailingGarbageError String", pipewire.TrailingGarbageError{
|
|
/* size: */ 0, 0, 0, 0,
|
|
/* type: */ byte(pipewire.SPA_TYPE_String), 0, 0, 0,
|
|
}, "data has extra values starting with String"},
|
|
{"TrailingGarbageError invalid", pipewire.TrailingGarbageError{
|
|
/* size: */ 0, 0, 0, 0,
|
|
/* type: */ 0xff, 0xff, 0xff, 0xff,
|
|
/* garbage: */ 0,
|
|
}, "data has extra values starting with invalid type field 0xffffffff"},
|
|
|
|
{"StringTerminationError", pipewire.StringTerminationError(0xff), "got byte 255 instead of NUL"},
|
|
|
|
{"InconsistentSizeError", pipewire.InconsistentSizeError{
|
|
Prefix: 0xbad,
|
|
Expect: 0xff,
|
|
}, "prefix claims size 2989 for a 255-byte long segment"},
|
|
|
|
{"UnexpectedTypeError zero", pipewire.UnexpectedTypeError{}, "received invalid type field 0x0 for a value of type invalid type field 0x0"},
|
|
{"UnexpectedTypeError", pipewire.UnexpectedTypeError{
|
|
Type: pipewire.SPA_TYPE_String,
|
|
Expect: pipewire.SPA_TYPE_Array,
|
|
}, "received String for a value of type Array"},
|
|
{"UnexpectedTypeError invalid", pipewire.UnexpectedTypeError{
|
|
Type: 0xdeadbeef,
|
|
Expect: pipewire.SPA_TYPE_Long,
|
|
}, "received invalid type field 0xdeadbeef for a value of type Long"},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if got := tc.err.Error(); got != tc.want {
|
|
t.Errorf("Error: %q, want %q", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
var benchmarkSample = func() (sample pipewire.CoreInfo) {
|
|
if err := sample.UnmarshalBinary(samplePWContainer[1][0][1]); err != nil {
|
|
panic(err)
|
|
}
|
|
return
|
|
}()
|
|
|
|
func BenchmarkMarshal(b *testing.B) {
|
|
for b.Loop() {
|
|
if _, err := benchmarkSample.MarshalBinary(); err != nil {
|
|
b.Fatalf("MarshalBinary: error = %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkMarshalJSON(b *testing.B) {
|
|
for b.Loop() {
|
|
if _, err := json.Marshal(benchmarkSample); err != nil {
|
|
b.Fatalf("json.Marshal: error = %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkGobEncode(b *testing.B) {
|
|
e := gob.NewEncoder(io.Discard)
|
|
type sampleRaw pipewire.CoreInfo
|
|
|
|
for b.Loop() {
|
|
if err := e.Encode((*sampleRaw)(&benchmarkSample)); err != nil {
|
|
b.Fatalf("(*gob.Encoder).Encode: error = %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkUnmarshal(b *testing.B) {
|
|
var got pipewire.CoreInfo
|
|
|
|
for b.Loop() {
|
|
if err := got.UnmarshalBinary(samplePWContainer[1][0][1]); err != nil {
|
|
b.Fatalf("UnmarshalBinary: error = %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkUnmarshalJSON(b *testing.B) {
|
|
var got pipewire.CoreInfo
|
|
data, err := json.Marshal(benchmarkSample)
|
|
if err != nil {
|
|
b.Fatalf("json.Marshal: error = %v", err)
|
|
}
|
|
|
|
for b.Loop() {
|
|
if err = json.Unmarshal(data, &got); err != nil {
|
|
b.Fatalf("json.Unmarshal: error = %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkGobDecode(b *testing.B) {
|
|
type sampleRaw pipewire.CoreInfo
|
|
var buf bytes.Buffer
|
|
e := gob.NewEncoder(&buf)
|
|
d := gob.NewDecoder(&buf)
|
|
|
|
for b.Loop() {
|
|
b.StopTimer()
|
|
if err := e.Encode((*sampleRaw)(&benchmarkSample)); err != nil {
|
|
b.Fatalf("(*gob.Encoder).Encode: error = %v", err)
|
|
}
|
|
b.StartTimer()
|
|
|
|
if err := d.Decode(new(sampleRaw)); err != nil {
|
|
b.Fatalf("(*gob.Encoder).Decode: error = %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// mustMarshalJSON calls [json.Marshal] and returns the result.
|
|
func mustMarshalJSON(v any) string {
|
|
if data, err := json.Marshal(v); err != nil {
|
|
panic(err)
|
|
} else {
|
|
return string(data)
|
|
}
|
|
}
|