All checks were successful
Test / Create distribution (push) Successful in 35s
Test / Sandbox (push) Successful in 2m41s
Test / Sandbox (race detector) (push) Successful in 4m40s
Test / Hakurei (push) Successful in 4m45s
Test / Hpkg (push) Successful in 5m1s
Test / Hakurei (race detector) (push) Successful in 6m13s
Test / Flake checks (push) Successful in 1m27s
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)
|
|
}
|
|
}
|