Compare commits

..

1 Commits

Author SHA1 Message Date
Joel Wetzell a117f668fa remove all fmt.println from receiveosc 2026-02-04 11:41:25 -06:00
15 changed files with 269 additions and 1108 deletions
-24
View File
@@ -1,24 +0,0 @@
name: Test
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Run tests
run: go test -v -coverprofile=coverage.txt .
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage.txt
+1 -2
View File
@@ -1,2 +1 @@
build build
coverage*
+4 -6
View File
@@ -1,7 +1,5 @@
A mostly complete OSC implementation and collection of command line OSC utilities written in Go. Mainly an exercise in learning Go. A collection of command line OSC utilities written in Go. Mainly an exercise in learning Go.
## Utilities # `sendosc`
# `makeosc`
### `sendosc` # `receiveosc`
### `makeosc`
### `receiveosc`
+10 -25
View File
@@ -4,51 +4,36 @@ import (
"errors" "errors"
) )
func (b *OSCBundle) ToBytes() ([]byte, error) { func (b *OSCBundle) ToBytes() []byte {
bytes := stringToOSCBytes("#bundle") bytes := stringToOSCBytes("#bundle")
timeTagBytes, err := timeTagToOSCBytes(b.TimeTag) bytes = append(bytes, timeTagToOSCBytes(b.TimeTag)...)
if err != nil {
return nil, err
}
bytes = append(bytes, timeTagBytes...)
for _, packet := range b.Contents { for _, packet := range b.Contents {
packetBytes, err := packet.ToBytes() packetBytes := packet.ToBytes()
if err != nil { packetLength := len(packet.ToBytes())
return nil, err
}
packetLength := len(packetBytes)
packetLengthBytes, err := int32ToOSCBytes(int32(packetLength)) bytes = append(bytes, int32ToOSCBytes(int32(packetLength))...)
if err != nil {
return nil, err
}
bytes = append(bytes, packetLengthBytes...)
bytes = append(bytes, packetBytes...) bytes = append(bytes, packetBytes...)
} }
return bytes, nil return bytes
} }
func BundleFromBytes(bytes []byte) (*OSCBundle, []byte, error) { func BundleFromBytes(bytes []byte) (*OSCBundle, []byte, error) {
if len(bytes) < 20 { if len(bytes) < 20 {
return nil, bytes, errors.New("OSC Bundle has to be at least 20 bytes") return nil, bytes, errors.New("bundle has to be at least 20 bytes")
} }
if bytes[0] != 35 { if bytes[0] != 35 {
return nil, bytes, errors.New("OSC Bundle must start with a #") return nil, bytes, errors.New("bundle must start with a #")
} }
bundleHeader, bytesAfterBundleHeader, err := readOSCString(bytes) bundleHeader, bytesAfterBundleHeader := readOSCString(bytes)
if err != nil {
return nil, bytes, err
}
if bundleHeader != "#bundle" { if bundleHeader != "#bundle" {
return nil, bytesAfterBundleHeader, errors.New("OSC Bundle must start with #bundle string") return nil, bytesAfterBundleHeader, errors.New("bundle must start with #bundle string")
} }
timeTag, bytesAfterTimeTag, err := readOSCTimeTag(bytesAfterBundleHeader) timeTag, bytesAfterTimeTag, err := readOSCTimeTag(bytesAfterBundleHeader)
+49 -172
View File
@@ -1,212 +1,89 @@
package osc package osc
import ( import (
"fmt"
"reflect" "reflect"
"testing" "testing"
) )
func TestGoodOSCBundleEncoding(t *testing.T) { func TestOSCBundleEncoding(t *testing.T) {
testCases := []struct { testCases := []struct {
name string description string
bundle *OSCBundle
expected []byte
}{
{
name: "simple contents single message",
bundle: &OSCBundle{
TimeTag: OSCTimeTag{
seconds: 32,
fractionalSeconds: 0,
},
Contents: []OSCPacket{&OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: float32(440)}}}},
},
expected: []byte{35, 98, 117, 110, 100, 108, 101, 0, 0, 0, 0,
32, 0, 0, 0, 0, 0, 0, 0, 32, 47, 111,
115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52,
47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0,
44, 102, 0, 0, 67, 220, 0, 0},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
got, err := testCase.bundle.ToBytes()
if err != nil {
t.Fatalf("failed to encode properly: %s", err.Error())
}
if !reflect.DeepEqual(got, testCase.expected) {
t.Fatalf("failed to encode properly got '%v', expected '%v'", got, testCase.expected)
}
})
}
}
func TestBadOSCBundleEncoding(t *testing.T) {
testCases := []struct {
name string
bundle *OSCBundle bundle *OSCBundle
errorString string expected []byte
}{ }{
{ {
name: "bundle contains message with bad address", "simple contents single message",
bundle: &OSCBundle{ &OSCBundle{
TimeTag: OSCTimeTag{
seconds: 32,
fractionalSeconds: 0,
},
Contents: []OSCPacket{&OSCMessage{Address: "hello", Args: []OSCArg{}}},
},
errorString: "OSC Message address must start with /",
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
got, err := testCase.bundle.ToBytes()
if err == nil {
t.Fatalf("OSCBundle.ToBytes() expected to fail but got: %+v", got)
}
if err.Error() != testCase.errorString {
t.Fatalf("OSCBundle.ToBytes() got error '%s', expected '%s'", err.Error(), testCase.errorString)
}
})
}
}
func TestGoodOSCBundleDecoding(t *testing.T) {
testCases := []struct {
name string
expected *OSCBundle
bytes []byte
}{
{
name: "simple contents single message",
expected: &OSCBundle{
TimeTag: OSCTimeTag{ TimeTag: OSCTimeTag{
seconds: 32, seconds: 32,
fractionalSeconds: 0, fractionalSeconds: 0,
}, },
Contents: []OSCPacket{&OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: float32(440)}}}}, Contents: []OSCPacket{&OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: float32(440)}}}},
}, },
bytes: []byte{35, 98, 117, 110, 100, 108, 101, 0, 0, 0, 0, []byte{35, 98, 117, 110, 100, 108, 101, 0, 0, 0, 0,
32, 0, 0, 0, 0, 0, 0, 0, 32, 47, 111, 32, 0, 0, 0, 0, 0, 0, 0, 32, 47, 111,
115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52, 115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52,
47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0, 47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0,
44, 102, 0, 0, 67, 220, 0, 0}, 44, 102, 0, 0, 67, 220, 0, 0},
}, },
}
for _, testCase := range testCases {
actual := testCase.bundle.ToBytes()
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("Test '%s' failed to encode properly", testCase.description)
fmt.Printf("expected: %v\n", testCase.expected)
fmt.Printf("actual: %v\n", actual)
}
}
}
func TestOSCBundleDecoding(t *testing.T) {
testCases := []struct {
description string
expected *OSCBundle
bytes []byte
}{
{ {
name: "simple contents nested bundle", "simple contents single message",
expected: &OSCBundle{ &OSCBundle{
TimeTag: OSCTimeTag{ TimeTag: OSCTimeTag{
seconds: 32, seconds: 32,
fractionalSeconds: 0, fractionalSeconds: 0,
}, },
Contents: []OSCPacket{&OSCBundle{ Contents: []OSCPacket{&OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: float32(440)}}}},
TimeTag: OSCTimeTag{
seconds: 64,
fractionalSeconds: 0,
},
Contents: []OSCPacket{&OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: float32(440)}}}},
}},
}, },
bytes: []byte{35, 98, 117, 110, 100, 108, 101, 0, // #bundle []byte{35, 98, 117, 110, 100, 108, 101, 0, 0, 0, 0,
0, 0, 0, 32, 0, 0, 0, 0, // time tag 32, 0, 0, 0, 0, 0, 0, 0, 32, 47, 111,
0, 0, 0, 52, // content size 115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52,
35, 98, 117, 110, 100, 108, 101, 0, // #bundle
0, 0, 0, 64, 0, 0, 0, 0, // time tag
0, 0, 0, 32, // content size
47, 111, 115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52,
47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0, 47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0,
44, 102, 0, 0, 67, 220, 0, 0}, 44, 102, 0, 0, 67, 220, 0, 0},
}, },
} }
for _, testCase := range testCases { for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
actual, remainingBytes, error := BundleFromBytes(testCase.bytes)
if error != nil { actual, remainingBytes, error := BundleFromBytes(testCase.bytes)
t.Fatalf("failed to decode properly: %s", error.Error())
}
if len(remainingBytes) > 0 { if error != nil {
t.Fatalf("should not have any remaining bytes") fmt.Println(error)
} t.Errorf("Test '%s' failed to encode properly", testCase.description)
}
if len(remainingBytes) > 0 {
t.Errorf("Test '%s' should not have any remaining bytes", testCase.description)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Errorf("Test '%s' failed to encode bundle properly", testCase.description)
fmt.Printf("expected: %v\n", testCase.expected)
fmt.Printf("actual: %v\n", actual)
}
if !reflect.DeepEqual(actual, testCase.expected) {
t.Fatalf("failed to decode properly got '%v', expected '%v'", actual, testCase.expected)
}
})
}
}
func TestBadOSCBundleDecoding(t *testing.T) {
testCases := []struct {
name string
bytes []byte
errorString string
}{
{
name: "empty byte array",
bytes: []byte{},
errorString: "OSC Bundle has to be at least 20 bytes",
},
{
name: "does not start with #",
bytes: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
errorString: "OSC Bundle must start with a #",
},
{
name: "does not start with #bundle",
bytes: []byte{35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
errorString: "OSC Bundle must start with #bundle string",
},
{
name: "bundle header not properly null terminated",
bytes: []byte{
35, 98, 117, 110, 100, 108, 101,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35},
errorString: "OSC string must be null-terminated",
},
{
name: "bundle contains incorrect size",
bytes: []byte{
35, 98, 117, 110, 100, 108, 101, 0, // #bundle
0, 0, 0, 0, 0, 0, 0, 0, // time tag
0, 0, 0, 100, // content size of 100 but only 10 bytes of content
35, 35, 35, 35, 35, 35, 35, 35, 35, 35},
errorString: "bundle doesn't have enough bytes for the content size it specifies",
},
{
name: "bundle doesn't contain message or bundle",
bytes: []byte{
35, 98, 117, 110, 100, 108, 101, 0, // #bundle
0, 0, 0, 0, 0, 0, 0, 0, // time tag
0, 0, 0, 10,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
errorString: "bundle contents does not look a bundle or message",
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
got, _, err := BundleFromBytes(testCase.bytes)
if err == nil {
t.Fatalf("BundleFromBytes expected to fail but got: %+v", got)
}
if err.Error() != testCase.errorString {
t.Fatalf("BundleFromBytes got error '%s', expected '%s'", err.Error(), testCase.errorString)
}
})
} }
} }
+1 -4
View File
@@ -175,10 +175,7 @@ func make(address string, args []string, types []string, slip bool) {
oscMessage.Args = append(oscMessage.Args, argToTypedArg(arg, oscType)) oscMessage.Args = append(oscMessage.Args, argToTypedArg(arg, oscType))
} }
oscMessageBuffer, err := oscMessage.ToBytes() oscMessageBuffer := oscMessage.ToBytes()
if err != nil {
panic(err)
}
if slip { if slip {
oscMessageBuffer = slipEncode(oscMessageBuffer) oscMessageBuffer = slipEncode(oscMessageBuffer)
+5 -6
View File
@@ -88,7 +88,7 @@ func main() {
func listenTCP(netAddress string, useSLIP bool, format string) { func listenTCP(netAddress string, useSLIP bool, format string) {
socket, err := net.Listen("tcp4", netAddress) socket, err := net.Listen("tcp4", netAddress)
if err != nil { if err != nil {
fmt.Println(err) // TODO(jwetzell): output error properly
return return
} }
@@ -97,7 +97,7 @@ func listenTCP(netAddress string, useSLIP bool, format string) {
for { for {
conn, err := socket.Accept() conn, err := socket.Accept()
if err != nil { if err != nil {
fmt.Println(err) // TODO(jwetzell): output error properly
continue continue
} }
go handleTCPConnection(conn, useSLIP, format) go handleTCPConnection(conn, useSLIP, format)
@@ -185,9 +185,8 @@ func handlePacket(message osc.OSCPacket, format string) {
handleBundle(bundle, format) handleBundle(bundle, format)
} else if msg, ok := message.(*osc.OSCMessage); ok { } else if msg, ok := message.(*osc.OSCMessage); ok {
handleMessage(msg, format) handleMessage(msg, format)
} else {
fmt.Println("Received unknown OSC Packet type")
} }
// TODO(jwetzell): handle other packet types?
} }
func handleMessage(message *osc.OSCMessage, format string) { func handleMessage(message *osc.OSCMessage, format string) {
@@ -209,13 +208,13 @@ func listenUDP(netAddress string, format string) {
s, err := net.ResolveUDPAddr("udp4", netAddress) s, err := net.ResolveUDPAddr("udp4", netAddress)
if err != nil { if err != nil {
fmt.Println(err) // TODO(jwetzell): output error properly
return return
} }
connection, err := net.ListenUDP("udp4", s) connection, err := net.ListenUDP("udp4", s)
if err != nil { if err != nil {
fmt.Println(err) // TODO(jwetzell): output error properly
return return
} }
+1 -4
View File
@@ -208,10 +208,7 @@ func send(host string, port int32, address string, args []string, types []string
} }
oscMessageBuffer, err := oscMessage.ToBytes() oscMessageBuffer := oscMessage.ToBytes()
if err != nil {
panic(err)
}
if slip { if slip {
oscMessageBuffer = slipEncode(oscMessageBuffer) oscMessageBuffer = slipEncode(oscMessageBuffer)
+1 -1
View File
@@ -2,4 +2,4 @@ module github.com/jwetzell/osc-go
go 1.25.1 go 1.25.1
require github.com/urfave/cli/v3 v3.8.0 require github.com/urfave/cli/v3 v3.6.2
+2 -2
View File
@@ -4,7 +4,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/urfave/cli/v3 v3.8.0 h1:XqKPrm0q4P0q5JpoclYoCAv0/MIvH/jZ2umzuf8pNTI= github.com/urfave/cli/v3 v3.6.2 h1:lQuqiPrZ1cIz8hz+HcrG0TNZFxU70dPZ3Yl+pSrH9A8=
github.com/urfave/cli/v3 v3.8.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= github.com/urfave/cli/v3 v3.6.2/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+13 -36
View File
@@ -5,16 +5,8 @@ import (
"strings" "strings"
) )
func (m *OSCMessage) ToBytes() ([]byte, error) { func (m *OSCMessage) ToBytes() []byte {
//TODO(jwetzell): add error handling
if len(m.Address) == 0 {
return nil, errors.New("OSC Message must have an address")
}
if m.Address[0] != '/' {
return nil, errors.New("OSC Message address must start with /")
}
oscBuffer := []byte{} oscBuffer := []byte{}
oscBuffer = append(oscBuffer, stringToOSCBytes(m.Address)...) oscBuffer = append(oscBuffer, stringToOSCBytes(m.Address)...)
@@ -26,28 +18,22 @@ func (m *OSCMessage) ToBytes() ([]byte, error) {
for _, arg := range m.Args { for _, arg := range m.Args {
sb.WriteString(arg.Type) sb.WriteString(arg.Type)
} }
oscBuffer = append(oscBuffer, stringToOSCBytes(sb.String())...)
argsBuffer, err := argsToBuffer(m.Args)
if err != nil {
return nil, err
}
oscBuffer = append(oscBuffer, argsBuffer...)
return oscBuffer, nil oscBuffer = append(oscBuffer, stringToOSCBytes(sb.String())...)
oscBuffer = append(oscBuffer, argsToBuffer(m.Args)...)
return oscBuffer
} }
func MessageFromBytes(bytes []byte) (*OSCMessage, error) { func MessageFromBytes(bytes []byte) (*OSCMessage, error) {
if len(bytes) == 0 { if len(bytes) == 0 {
return nil, errors.New("cannot create OSC Message from empty byte array") return nil, errors.New("cannot create OSC Message from empty byte array")
} }
if bytes[0] != 47 {
return nil, errors.New("OSC Message must start with /")
}
address, typeAndArgBytes, err := readOSCString(bytes) address, typeAndArgBytes := readOSCString(bytes)
if err != nil { if address[0] != 47 {
return nil, err return nil, errors.New("OSC Message address must start with /")
} }
oscMessage := OSCMessage{ oscMessage := OSCMessage{
@@ -55,16 +41,7 @@ func MessageFromBytes(bytes []byte) (*OSCMessage, error) {
Args: []OSCArg{}, Args: []OSCArg{},
} }
if len(typeAndArgBytes) == 0 { typeString, argBytes := readOSCString(typeAndArgBytes)
// NOTE(jwetzell): no type string return early.
return &oscMessage, nil
}
typeString, argBytes, err := readOSCString(typeAndArgBytes)
if err != nil {
return nil, err
}
for index, oscType := range typeString { for index, oscType := range typeString {
if index == 0 { if index == 0 {
@@ -72,9 +49,9 @@ func MessageFromBytes(bytes []byte) (*OSCMessage, error) {
return nil, errors.New("type string is malformed") return nil, errors.New("type string is malformed")
} }
} else { } else {
oscArg, remainingBytes, err := readOSCArg(argBytes, string(oscType)) oscArg, remainingBytes, error := readOSCArg(argBytes, string(oscType))
if err != nil { if error != nil {
return nil, err return nil, error
} }
argBytes = remainingBytes argBytes = remainingBytes
oscMessage.Args = append(oscMessage.Args, oscArg) oscMessage.Args = append(oscMessage.Args, oscArg)
+105 -333
View File
@@ -1,29 +1,30 @@
package osc package osc
import ( import (
"fmt"
"math" "math"
"reflect" "reflect"
"testing" "testing"
) )
func TestGoodOSCMessageEncoding(t *testing.T) { func TestOSCMessageEncoding(t *testing.T) {
testCases := []struct { testCases := []struct {
name string description string
message *OSCMessage message *OSCMessage
expected []byte expected []byte
}{ }{
{ {
name: "simple hello", "simple hello",
message: &OSCMessage{ &OSCMessage{
Address: "/hello", Address: "/hello",
Args: []OSCArg{}, Args: []OSCArg{},
}, },
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 0, 0, 0}, []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 0, 0, 0},
}, },
{ {
name: "simple address string arg", "simple address string arg",
message: &OSCMessage{ &OSCMessage{
Address: "/hello", Address: "/hello",
Args: []OSCArg{ Args: []OSCArg{
{ {
@@ -32,58 +33,58 @@ func TestGoodOSCMessageEncoding(t *testing.T) {
}, },
}, },
}, },
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 115, 0, 0, 97, 114, 103, 49, 0, 0, 0, 0}, []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 115, 0, 0, 97, 114, 103, 49, 0, 0, 0, 0},
}, },
{ {
name: "simple address integer arg", description: "simple address integer arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "i", Value: 35}}}, message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "i", Value: 35}}},
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 105, 0, 0, 0, 0, 0, 35}, expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 105, 0, 0, 0, 0, 0, 35},
}, },
{ {
name: "simple address float arg", description: "simple address float arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "f", Value: 34.5}}}, message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "f", Value: 34.5}}},
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 102, 0, 0, 66, 10, 0, 0}, expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 102, 0, 0, 66, 10, 0, 0},
}, },
{ {
name: "simple address blob arg", description: "simple address blob arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "b", Value: []byte{98, 108, 111, 98}}}}, message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "b", Value: []byte{98, 108, 111, 98}}}},
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 98, 0, 0, 0, 0, 0, 4, 98, 108, 111, 98}, expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 98, 0, 0, 0, 0, 0, 4, 98, 108, 111, 98},
}, },
{ {
name: "simple address True arg", description: "simple address True arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "T", Value: true}}}, message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "T", Value: true}}},
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 84, 0, 0}, expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 84, 0, 0},
}, },
{ {
name: "simple address False arg", description: "simple address False arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "F", Value: false}}}, message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "F", Value: false}}},
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 70, 0, 0}, expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 70, 0, 0},
}, },
{ {
name: "simple address color arg", description: "simple address color arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "r", Value: OSCColor{r: 20, g: 21, b: 22, a: 10}}}}, message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "r", Value: OSCColor{r: 20, g: 21, b: 22, a: 10}}}},
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 114, 0, 0, 20, 21, 22, 10}, expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 114, 0, 0, 20, 21, 22, 10},
}, },
{ {
name: "simple address nil arg", description: "simple address nil arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "N", Value: nil}}}, message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "N", Value: nil}}},
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 78, 0, 0}, expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 78, 0, 0},
}, },
{ {
name: "simple address int64 arg", description: "simple address int64 arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "h", Value: 281474976710655}}}, message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "h", Value: 281474976710655}}},
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 104, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255}, expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 104, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255},
}, },
{ {
name: "simple address float64 arg", description: "simple address float64 arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "d", Value: 12.7654763}}}, message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "d", Value: 12.7654763}}},
expected: []byte{ expected: []byte{
47, 104, 101, 108, 108, 111, 0, 0, 44, 100, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6, 47, 104, 101, 108, 108, 111, 0, 0, 44, 100, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6,
}, },
}, },
// TODO(jwetzell): get array args working working // TODO(jwetzell): get array args working working
// { // {
// name: "simple address array arg", // description: "simple address array arg",
// message: OSCMessage{ // message: OSCMessage{
// Address: "/hello", // Address: "/hello",
// Args: []OSCArg{ // Args: []OSCArg{
@@ -99,15 +100,15 @@ func TestGoodOSCMessageEncoding(t *testing.T) {
// }, // },
// }, // },
{ {
name: "osc 1.0 spec example 1", description: "osc 1.0 spec example 1",
message: &OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: 440}}}, message: &OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: 440}}},
expected: []byte{ expected: []byte{
47, 111, 115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52, 47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0, 44, 47, 111, 115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52, 47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0, 44,
102, 0, 0, 67, 220, 0, 0, 102, 0, 0, 67, 220, 0, 0,
}, },
}, },
{ {
name: "osc 1.0 spec example 2", description: "osc 1.0 spec example 2",
message: &OSCMessage{ message: &OSCMessage{
Address: "/foo", Address: "/foo",
Args: []OSCArg{ Args: []OSCArg{
@@ -127,181 +128,81 @@ func TestGoodOSCMessageEncoding(t *testing.T) {
} }
for _, testCase := range testCases { for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
got, err := testCase.message.ToBytes()
if err != nil { actual := testCase.message.ToBytes()
t.Fatalf("failed to encode properly: %s", err.Error())
}
if !reflect.DeepEqual(got, testCase.expected) { if !reflect.DeepEqual(actual, testCase.expected) {
t.Fatalf("failed to encode properly got '%v', expected '%v'", got, testCase.expected) t.Errorf("Test '%s' failed to encode properly", testCase.description)
} fmt.Printf("expected: %v\n", testCase.expected)
}) fmt.Printf("actual: %v\n", actual)
}
} }
} }
func TestBadOSCMessageEncoding(t *testing.T) { func TestOSCMessageDecoding(t *testing.T) {
testCases := []struct { testCases := []struct {
name string description string
message *OSCMessage bytes []byte
errorString string expected OSCMessage
}{ }{
{ {
name: "empty message", description: "simple address no args",
message: &OSCMessage{}, bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 0, 0, 0},
errorString: "OSC Message must have an address", expected: OSCMessage{Address: "/hello", Args: []OSCArg{}},
}, },
{ {
name: "address does not start with /", description: "simple address string arg",
message: &OSCMessage{Address: "hello"}, bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 115, 0, 0, 97, 114, 103, 49, 0, 0, 0, 0},
errorString: "OSC Message address must start with /", expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "s", Value: "arg1"}}},
}, },
{ {
name: "arg with unsupported type", description: "simple address integer arg",
message: &OSCMessage{ bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 105, 0, 0, 0, 0, 0, 35},
Address: "/hello", expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "i", Value: int32(35)}}},
Args: []OSCArg{{Type: "x", Value: "unsupported"}},
},
errorString: "unsupported OSC argument type: x",
}, },
{ {
name: "string arg that is not a string", description: "simple address float arg",
message: &OSCMessage{ bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 102, 0, 0, 66, 10, 0, 0},
Address: "/hello", expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "f", Value: float32(34.5)}}},
Args: []OSCArg{{Type: "s", Value: 123}},
},
errorString: "OSC arg had string type but non-string value",
}, },
{ {
name: "int32 arg that is not a number", description: "simple address blob arg",
message: &OSCMessage{ bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 98, 0, 0, 0, 0, 0, 4, 98, 108, 111, 98},
Address: "/hello", expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "b", Value: []byte{98, 108, 111, 98}}}},
Args: []OSCArg{{Type: "i", Value: "not an int"}},
},
errorString: "OSC arg had int32 type but non-number value",
}, },
{ {
name: "float32 arg that is not a number", description: "simple address True arg",
message: &OSCMessage{ bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 84, 0, 0},
Address: "/hello", expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "T", Value: true}}},
Args: []OSCArg{{Type: "f", Value: "not a float"}},
},
errorString: "OSC arg had float32 type but non-number value",
}, },
{ {
name: "int64 arg that is not a number", description: "simple address False arg",
message: &OSCMessage{ bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 70, 0, 0},
Address: "/hello", expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "F", Value: false}}},
Args: []OSCArg{{Type: "h", Value: "not an int"}},
},
errorString: "OSC arg had int64 type but non-number value",
}, },
{ {
name: "float64 arg that is not a number", description: "simple address color arg",
message: &OSCMessage{ bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 114, 0, 0, 20, 21, 22, 10},
Address: "/hello", expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "r", Value: OSCColor{r: 20, g: 21, b: 22, a: 10}}}},
Args: []OSCArg{{Type: "d", Value: "not a float"}},
},
errorString: "OSC arg had float64 type but non-number value",
}, },
{ {
name: "blob arg that is not a byte array", description: "simple address nil arg",
message: &OSCMessage{ bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 78, 0, 0},
Address: "/hello", expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "N", Value: nil}}},
Args: []OSCArg{{Type: "b", Value: "not a blob"}},
},
errorString: "OSC arg had blob type but non-blob value",
}, },
{ {
name: "color arg that is not an OSCColor", description: "simple address Inifinitum arg",
message: &OSCMessage{ bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 73, 0, 0},
Address: "/hello", expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "I", Value: math.MaxInt32}}},
Args: []OSCArg{{Type: "r", Value: "not a color"}},
},
errorString: "OSC arg had color type but non-color value",
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
got, err := testCase.message.ToBytes()
if err == nil {
t.Fatalf("OSCMessage.ToBytes() expected to fail but got: %+v", got)
}
if err.Error() != testCase.errorString {
t.Fatalf("OSCMessage.ToBytes() got error '%s', expected '%s'", err.Error(), testCase.errorString)
}
})
}
}
func TestGoodOSCMessageDecoding(t *testing.T) {
testCases := []struct {
name string
bytes []byte
expected OSCMessage
}{
{
name: "simple address no args",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 0, 0, 0},
expected: OSCMessage{Address: "/hello", Args: []OSCArg{}},
}, },
{ {
name: "simple address string arg", description: "simple address int64 arg",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 115, 0, 0, 97, 114, 103, 49, 0, 0, 0, 0}, bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 104, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255},
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "s", Value: "arg1"}}}, expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "h", Value: int64(281474976710655)}}},
}, },
{ {
name: "simple address integer arg", description: "simple address float64 arg",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 105, 0, 0, 0, 0, 0, 35},
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "i", Value: int32(35)}}},
},
{
name: "simple address float arg",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 102, 0, 0, 66, 10, 0, 0},
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "f", Value: float32(34.5)}}},
},
{
name: "simple address blob arg",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 98, 0, 0, 0, 0, 0, 4, 98, 108, 111, 98},
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "b", Value: []byte{98, 108, 111, 98}}}},
},
{
name: "simple address True arg",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 84, 0, 0},
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "T", Value: true}}},
},
{
name: "simple address False arg",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 70, 0, 0},
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "F", Value: false}}},
},
{
name: "simple address color arg",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 114, 0, 0, 20, 21, 22, 10},
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "r", Value: OSCColor{r: 20, g: 21, b: 22, a: 10}}}},
},
{
name: "simple address nil arg",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 78, 0, 0},
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "N", Value: nil}}},
},
{
name: "simple address Inifinitum arg",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 73, 0, 0},
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "I", Value: math.MaxInt32}}},
},
{
name: "simple address int64 arg",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 104, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255},
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "h", Value: int64(281474976710655)}}},
},
{
name: "simple address float64 arg",
bytes: []byte{ bytes: []byte{
47, 104, 101, 108, 108, 111, 0, 0, 44, 100, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6, 47, 104, 101, 108, 108, 111, 0, 0, 44, 100, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6,
}, },
@@ -309,7 +210,7 @@ func TestGoodOSCMessageDecoding(t *testing.T) {
}, },
// TODO(jwetzell): support OSC array // TODO(jwetzell): support OSC array
// { // {
// name: "simple address array arg", // description: "simple address array arg",
// bytes: []byte{ // bytes: []byte{
// 47, 104, 101, 108, 108, 111, 0, 0, 44, 91, 100, 105, 93, 0, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6, // 47, 104, 101, 108, 108, 111, 0, 0, 44, 91, 100, 105, 93, 0, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6,
// 0, 0, 3, 232, // 0, 0, 3, 232,
@@ -325,15 +226,15 @@ func TestGoodOSCMessageDecoding(t *testing.T) {
// }, // },
// }, // },
{ {
name: "simple address no type string", description: "simple address no type string",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0}, bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0},
expected: OSCMessage{ expected: OSCMessage{
Address: "/hello", Address: "/hello",
Args: []OSCArg{}, Args: []OSCArg{},
}, },
}, },
{ {
name: "osc 1.0 spec example 1", description: "osc 1.0 spec example 1",
bytes: []byte{ bytes: []byte{
47, 111, 115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52, 47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0, 44, 47, 111, 115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52, 47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0, 44,
102, 0, 0, 67, 220, 0, 0, 102, 0, 0, 67, 220, 0, 0,
@@ -341,7 +242,7 @@ func TestGoodOSCMessageDecoding(t *testing.T) {
expected: OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: float32(440)}}}, expected: OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: float32(440)}}},
}, },
{ {
name: "osc 1.0 spec example 2", description: "osc 1.0 spec example 2",
bytes: []byte{ bytes: []byte{
47, 102, 111, 111, 0, 0, 0, 0, 44, 105, 105, 115, 102, 102, 0, 0, 0, 0, 3, 232, 255, 255, 255, 255, 104, 101, 108, 47, 102, 111, 111, 0, 0, 0, 0, 44, 105, 105, 115, 102, 102, 0, 0, 0, 0, 3, 232, 255, 255, 255, 255, 104, 101, 108,
108, 111, 0, 0, 0, 63, 157, 243, 182, 64, 181, 178, 45, 108, 111, 0, 0, 0, 63, 157, 243, 182, 64, 181, 178, 45,
@@ -361,153 +262,24 @@ func TestGoodOSCMessageDecoding(t *testing.T) {
} }
for _, testCase := range testCases { for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
actual, err := MessageFromBytes(testCase.bytes) actual, error := MessageFromBytes(testCase.bytes)
if err != nil { if error != nil {
t.Fatalf("failed to encode properly: %s", err.Error()) fmt.Println(error)
} t.Errorf("Test '%s' failed to encode properly", testCase.description)
}
if !reflect.DeepEqual(actual.Address, testCase.expected.Address) { if !reflect.DeepEqual(actual.Address, testCase.expected.Address) {
t.Fatalf("failed to encode address propertly got '%s', expected '%s'", actual.Address, testCase.expected.Address) t.Errorf("Test '%s' failed to encode address properly", testCase.description)
} fmt.Printf("expected: %v\n", testCase.expected.Address)
fmt.Printf("actual: %v\n", actual.Address)
}
if !reflect.DeepEqual(actual.Args, testCase.expected.Args) { if !reflect.DeepEqual(actual.Args, testCase.expected.Args) {
t.Fatalf("failed to encode args properly got '%+v', expected '%+v'", actual.Args, testCase.expected.Args) t.Errorf("Test '%s' failed to encode args properly", testCase.description)
} fmt.Printf("expected: %v\n", testCase.expected.Args)
}) fmt.Printf("actual: %v\n", actual.Args)
} }
}
func TestBadOSCMessageDecoding(t *testing.T) {
testCases := []struct {
name string
bytes []byte
errorString string
}{
{
name: "empty byte array",
bytes: []byte{},
errorString: "cannot create OSC Message from empty byte array",
},
{
name: "does not start with /",
bytes: []byte{0, 104, 101, 108, 108, 111, 0, 0},
errorString: "OSC Message must start with /",
},
{
name: "address string not padded",
bytes: []byte{47, 104, 101, 108, 108, 111, 0},
errorString: "OSC string is not properly padded",
},
{
name: "type string not padded",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 0},
errorString: "OSC string is not properly padded",
},
{
name: "type string does not start with ,",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 45, 0, 0, 0},
errorString: "type string is malformed",
},
{
name: "string arg not null-terminated",
bytes: []byte{
47, 104, 101, 108, 108, 111, 0, 0, 44, 115, 0, 0, 97, 114, 103, 49,
},
errorString: "OSC string must be null-terminated",
},
{
name: "string arg not padded",
bytes: []byte{
47, 104, 101, 108, 108, 111, 0, 0, 44, 115, 0, 0, 104, 105, 0,
},
errorString: "OSC string is not properly padded",
},
{
name: "int32 arg not 4 bytes",
bytes: []byte{
47, 104, 101, 108, 108, 111, 0, 0, 44, 105, 0, 0, 0,
},
errorString: "OSC int32 arg is not 4 bytes",
},
{
name: "int64 arg not 8 bytes",
bytes: []byte{
47, 104, 101, 108, 108, 111, 0, 0, 44, 104, 0, 0, 0, 0, 0, 0,
},
errorString: "OSC int64 arg is not 8 bytes",
},
{
name: "float32 arg not 4 bytes",
bytes: []byte{
47, 104, 101, 108, 108, 111, 0, 0, 44, 102, 0, 0, 66,
},
errorString: "OSC float32 arg is not 4 bytes",
},
{
name: "float64 arg not 8 bytes",
bytes: []byte{
47, 104, 101, 108, 108, 111, 0, 0, 44, 100, 0, 0, 0,
},
errorString: "OSC float64 arg is not 8 bytes",
},
{
name: "blob arg size not valid",
bytes: []byte{
47, 104, 101, 108, 108, 111, 0, 0, 44, 98, 0, 0, 0, 0, 0,
},
errorString: "OSC blob arg size not valid: OSC int32 arg is not 4 bytes",
},
{
name: "blob arg size mismatch",
bytes: []byte{
47, 104, 101, 108, 108, 111, 0, 0, 44, 98, 0, 0, 0, 0, 0, 4, 98, 108, 111,
},
errorString: "OSC blob arg size not valid: size specified is larger than remaining bytes",
},
{
name: "color arg not 4 bytes",
bytes: []byte{
47, 104, 101, 108, 108, 111, 0, 0, 44, 114, 0, 0, 20, 21,
},
errorString: "OSC color arg is not 4 bytes",
},
{
name: "time tag arg seconds not complete",
bytes: []byte{
47, 104, 101, 108, 108, 111, 0, 0, 44, 116, 0, 0, 0,
},
errorString: "OSC time tag seconds are not valid: OSC int32 arg is not 4 bytes",
},
{
name: "time tag arg fractional seconds not complete",
bytes: []byte{
47, 104, 101, 108, 108, 111, 0, 0, 44, 116, 0, 0, 0, 32, 0, 0, 0,
},
errorString: "OSC time tag fractional seconds are not valid: OSC int32 arg is not 4 bytes",
},
{
name: "unknown arg type",
bytes: []byte{
47, 104, 101, 108, 108, 111, 0, 0, 44, 120, 0, 0,
},
errorString: "unsupported OSC argument type: x",
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
got, err := MessageFromBytes(testCase.bytes)
if err == nil {
t.Fatalf("MessageFromBytes expected to fail but got: %+v", got)
}
if err.Error() != testCase.errorString {
t.Fatalf("MessageFromBytes got error '%s', expected '%s'", err.Error(), testCase.errorString)
}
})
} }
} }
+76 -174
View File
@@ -26,51 +26,47 @@ func stringToOSCBytes(rawString string) []byte {
return []byte(sb.String()) return []byte(sb.String())
} }
func int32ToOSCBytes(number int32) ([]byte, error) { func int32ToOSCBytes(number int32) []byte {
var buf bytes.Buffer var buf bytes.Buffer
err := binary.Write(&buf, binary.BigEndian, number) err := binary.Write(&buf, binary.BigEndian, number)
if err != nil { if err != nil {
return nil, err panic(err)
} }
return buf.Bytes(), nil return buf.Bytes()
} }
func int64ToOSCBytes(number int64) ([]byte, error) { func int64ToOSCBytes(number int64) []byte {
var buf bytes.Buffer var buf bytes.Buffer
err := binary.Write(&buf, binary.BigEndian, number) err := binary.Write(&buf, binary.BigEndian, number)
if err != nil { if err != nil {
return nil, err panic(err)
} }
return buf.Bytes(), nil return buf.Bytes()
} }
func float32ToOSCBytes(number float32) ([]byte, error) { func float32ToOSCBytes(number float32) []byte {
var buf bytes.Buffer var buf bytes.Buffer
err := binary.Write(&buf, binary.BigEndian, number) err := binary.Write(&buf, binary.BigEndian, number)
if err != nil { if err != nil {
return nil, err panic(err)
} }
return buf.Bytes(), nil return buf.Bytes()
} }
func float64ToOSCBytes(number float64) ([]byte, error) { func float64ToOSCBytes(number float64) []byte {
var buf bytes.Buffer var buf bytes.Buffer
err := binary.Write(&buf, binary.BigEndian, number) err := binary.Write(&buf, binary.BigEndian, number)
if err != nil { if err != nil {
return nil, err panic(err)
} }
return buf.Bytes(), nil return buf.Bytes()
} }
func byteArrayToOSCBytes(bytes []byte) ([]byte, error) { func byteArrayToOSCBytes(bytes []byte) []byte {
oscBytes := []byte{} oscBytes := []byte{}
bytesSize := len(bytes) bytesSize := len(bytes)
bytesSizeBytes, err := int32ToOSCBytes(int32(bytesSize)) oscBytes = append(oscBytes, int32ToOSCBytes(int32(bytesSize))...)
if err != nil {
return nil, err
}
oscBytes = append(oscBytes, bytesSizeBytes...)
oscBytes = append(oscBytes, bytes...) oscBytes = append(oscBytes, bytes...)
padLength := 4 - (bytesSize % 4) padLength := 4 - (bytesSize % 4)
@@ -80,24 +76,17 @@ func byteArrayToOSCBytes(bytes []byte) ([]byte, error) {
} }
} }
return oscBytes, nil return oscBytes
} }
func timeTagToOSCBytes(timeTag OSCTimeTag) ([]byte, error) { func timeTagToOSCBytes(timeTag OSCTimeTag) []byte {
timeTagBytes, err := int32ToOSCBytes(timeTag.seconds) timeTagBytes := int32ToOSCBytes(timeTag.seconds)
if err != nil { timeTagBytes = append(timeTagBytes, int32ToOSCBytes(timeTag.fractionalSeconds)...)
return nil, err
}
fractionalSecondsBytes, err := int32ToOSCBytes(timeTag.fractionalSeconds)
if err != nil {
return nil, err
}
timeTagBytes = append(timeTagBytes, fractionalSecondsBytes...)
return timeTagBytes, nil return timeTagBytes
} }
func argsToBuffer(args []OSCArg) ([]byte, error) { func argsToBuffer(args []OSCArg) []byte {
//TODO(jwetzell): add error handling //TODO(jwetzell): add error handling
var argBuffers = []byte{} var argBuffers = []byte{}
@@ -107,67 +96,35 @@ func argsToBuffer(args []OSCArg) ([]byte, error) {
if value, ok := arg.Value.(string); ok { if value, ok := arg.Value.(string); ok {
argBuffers = append(argBuffers, stringToOSCBytes(value)...) argBuffers = append(argBuffers, stringToOSCBytes(value)...)
} else { } else {
return nil, errors.New("OSC arg had string type but non-string value") fmt.Println("OSC arg had string type but non-string value.")
} }
case "i": case "i":
if value, ok := arg.Value.(int); ok { if value, ok := arg.Value.(int); ok {
valueBytes, err := int32ToOSCBytes(int32(value)) argBuffers = append(argBuffers, int32ToOSCBytes(int32(value))...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else if value, ok := arg.Value.(int32); ok { } else if value, ok := arg.Value.(int32); ok {
valueBytes, err := int32ToOSCBytes(int32(value)) argBuffers = append(argBuffers, int32ToOSCBytes(value)...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else { } else {
return nil, errors.New("OSC arg had int32 type but non-number value") fmt.Println("OSC arg had integer type but non-integer value.")
} }
case "f": case "f":
if value, ok := arg.Value.(float32); ok { if value, ok := arg.Value.(float32); ok {
valueBytes, err := float32ToOSCBytes(value) argBuffers = append(argBuffers, float32ToOSCBytes(float32(value))...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else if value, ok := arg.Value.(float64); ok { } else if value, ok := arg.Value.(float64); ok {
valueBytes, err := float32ToOSCBytes(float32(value)) argBuffers = append(argBuffers, float32ToOSCBytes(float32(value))...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else if value, ok := arg.Value.(int); ok { } else if value, ok := arg.Value.(int); ok {
valueBytes, err := float32ToOSCBytes(float32(value)) argBuffers = append(argBuffers, float32ToOSCBytes(float32(value))...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else if value, ok := arg.Value.(int32); ok { } else if value, ok := arg.Value.(int32); ok {
valueBytes, err := float32ToOSCBytes(float32(value)) argBuffers = append(argBuffers, float32ToOSCBytes(float32(value))...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else if value, ok := arg.Value.(int64); ok { } else if value, ok := arg.Value.(int64); ok {
valueBytes, err := float32ToOSCBytes(float32(value)) argBuffers = append(argBuffers, float32ToOSCBytes(float32(value))...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else { } else {
return nil, errors.New("OSC arg had float32 type but non-number value") fmt.Println("OSC arg had float type but non-float value.")
} }
case "b": case "b":
if value, ok := arg.Value.([]byte); ok { if value, ok := arg.Value.([]byte); ok {
valueBytes, err := byteArrayToOSCBytes(value) argBuffers = append(argBuffers, byteArrayToOSCBytes(value)...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else { } else {
return nil, errors.New("OSC arg had blob type but non-blob value") fmt.Println("OSC arg had blob type but non-blob value.")
} }
case "T": case "T":
argBuffers = append(argBuffers, make([]byte, 0)...) argBuffers = append(argBuffers, make([]byte, 0)...)
@@ -179,112 +136,68 @@ func argsToBuffer(args []OSCArg) ([]byte, error) {
argBuffers = append(argBuffers, make([]byte, 0)...) argBuffers = append(argBuffers, make([]byte, 0)...)
case "r": case "r":
color, ok := arg.Value.(OSCColor) color, ok := arg.Value.(OSCColor)
if !ok {
return nil, errors.New("OSC arg had color type but non-color value")
}
if ok { if ok {
colorBytes := []byte{color.r, color.g, color.b, color.a} colorBytes := []byte{color.r, color.g, color.b, color.a}
argBuffers = append(argBuffers, colorBytes...) argBuffers = append(argBuffers, colorBytes...)
} }
case "h": case "h":
if value, ok := arg.Value.(int); ok { if value, ok := arg.Value.(int); ok {
valueBytes, err := int64ToOSCBytes(int64(value)) argBuffers = append(argBuffers, int64ToOSCBytes(int64(value))...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else if value, ok := arg.Value.(int32); ok { } else if value, ok := arg.Value.(int32); ok {
valueBytes, err := int64ToOSCBytes(int64(value)) argBuffers = append(argBuffers, int64ToOSCBytes(int64(value))...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else if value, ok := arg.Value.(int64); ok { } else if value, ok := arg.Value.(int64); ok {
valueBytes, err := int64ToOSCBytes(value) argBuffers = append(argBuffers, int64ToOSCBytes(value)...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else { } else {
return nil, errors.New("OSC arg had int64 type but non-number value") fmt.Println("OSC arg had integer type but non-integer value.")
} }
case "d": case "d":
if value, ok := arg.Value.(float32); ok { if value, ok := arg.Value.(float32); ok {
valueBytes, err := float64ToOSCBytes(float64(value)) argBuffers = append(argBuffers, float64ToOSCBytes(float64(value))...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else if value, ok := arg.Value.(float64); ok { } else if value, ok := arg.Value.(float64); ok {
valueBytes, err := float64ToOSCBytes(value) argBuffers = append(argBuffers, float64ToOSCBytes(float64(value))...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else if value, ok := arg.Value.(int); ok { } else if value, ok := arg.Value.(int); ok {
valueBytes, err := float64ToOSCBytes(float64(value)) argBuffers = append(argBuffers, float64ToOSCBytes(float64(value))...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else if value, ok := arg.Value.(int32); ok { } else if value, ok := arg.Value.(int32); ok {
valueBytes, err := float64ToOSCBytes(float64(value)) argBuffers = append(argBuffers, float64ToOSCBytes(float64(value))...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else if value, ok := arg.Value.(int64); ok { } else if value, ok := arg.Value.(int64); ok {
valueBytes, err := float64ToOSCBytes(float64(value)) argBuffers = append(argBuffers, float64ToOSCBytes(float64(value))...)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
} else { } else {
return nil, errors.New("OSC arg had float64 type but non-number value") fmt.Println("OSC arg had float type but non-float value.")
} }
default: default:
return nil, fmt.Errorf("unsupported OSC argument type: %s", oscType) fmt.Printf("unhandled osc type: %s.\n", oscType)
} }
} }
return argBuffers, nil return argBuffers
} }
func readOSCString(bytes []byte) (string, []byte, error) { func readOSCString(bytes []byte) (string, []byte) {
//TODO(jwetzell): add error handling
oscString := "" oscString := ""
stringEndIndex := 0 stringEndIndex := 0
nullByteFound := false
for index, byteIn := range bytes { for index, byteIn := range bytes {
if byteIn == 0 { if byteIn == 0 {
nullByteFound = true
oscString = string(bytes[0:index]) oscString = string(bytes[0:index])
stringEndIndex = index + 1 stringEndIndex = index + 1
break break
} }
} }
if !nullByteFound {
return "", bytes, errors.New("OSC string must be null-terminated")
}
stringPadding := 4 - (stringEndIndex % 4) stringPadding := 4 - (stringEndIndex % 4)
if stringPadding < 4 { if stringPadding < 4 {
stringEndIndex = stringEndIndex + stringPadding stringEndIndex = stringEndIndex + stringPadding
} }
if stringEndIndex > len(bytes) {
return "", bytes, errors.New("OSC string is not properly padded")
}
remainingBytes := bytes[stringEndIndex:] remainingBytes := bytes[stringEndIndex:]
return oscString, remainingBytes, nil return oscString, remainingBytes
} }
func readOSCInt32(bytes []byte) (int32, []byte, error) { func readOSCInt32(bytes []byte) (int32, []byte, error) {
if len(bytes) < 4 { if len(bytes) < 4 {
return 0, bytes, errors.New("OSC int32 arg is not 4 bytes") return 0, bytes, errors.New("int data must be at least 4 bytes large")
} }
bits := binary.BigEndian.Uint32(bytes[0:4]) bits := binary.BigEndian.Uint32(bytes[0:4])
return int32(bits), bytes[4:], nil return int32(bits), bytes[4:], nil
@@ -292,7 +205,7 @@ func readOSCInt32(bytes []byte) (int32, []byte, error) {
func readOSCInt64(bytes []byte) (int64, []byte, error) { func readOSCInt64(bytes []byte) (int64, []byte, error) {
if len(bytes) < 8 { if len(bytes) < 8 {
return 0, bytes, errors.New("OSC int64 arg is not 8 bytes") return 0, bytes, errors.New("int data must be at least 4 bytes large")
} }
bits := binary.BigEndian.Uint64(bytes[0:8]) bits := binary.BigEndian.Uint64(bytes[0:8])
return int64(bits), bytes[8:], nil return int64(bits), bytes[8:], nil
@@ -300,7 +213,7 @@ func readOSCInt64(bytes []byte) (int64, []byte, error) {
func readOSCFloat32(bytes []byte) (float32, []byte, error) { func readOSCFloat32(bytes []byte) (float32, []byte, error) {
if len(bytes) < 4 { if len(bytes) < 4 {
return 0, bytes, errors.New("OSC float32 arg is not 4 bytes") return 0, bytes, errors.New("float data must be at least 4 bytes large")
} }
bits := binary.BigEndian.Uint32(bytes[0:4]) bits := binary.BigEndian.Uint32(bytes[0:4])
return math.Float32frombits(bits), bytes[4:], nil return math.Float32frombits(bits), bytes[4:], nil
@@ -308,7 +221,7 @@ func readOSCFloat32(bytes []byte) (float32, []byte, error) {
func readOSCFloat64(bytes []byte) (float64, []byte, error) { func readOSCFloat64(bytes []byte) (float64, []byte, error) {
if len(bytes) < 4 { if len(bytes) < 4 {
return 0, bytes, errors.New("OSC float64 arg is not 8 bytes") return 0, bytes, errors.New("float data must be at least 4 bytes large")
} }
bits := binary.BigEndian.Uint64(bytes[0:8]) bits := binary.BigEndian.Uint64(bytes[0:8])
return math.Float64frombits(bits), bytes[8:], nil return math.Float64frombits(bits), bytes[8:], nil
@@ -318,11 +231,11 @@ func readOSCBlob(bytes []byte) ([]byte, []byte, error) {
blobLength, remainingBytes, err := readOSCInt32(bytes) blobLength, remainingBytes, err := readOSCInt32(bytes)
if err != nil { if err != nil {
return []byte{}, bytes, errors.New("OSC blob arg size not valid: " + err.Error()) return []byte{}, bytes, errors.New("problem reading blob data size")
} }
if len(remainingBytes) < int(blobLength) { if len(remainingBytes) < int(blobLength) {
return []byte{}, bytes, errors.New("OSC blob arg size not valid: size specified is larger than remaining bytes") return []byte{}, bytes, errors.New("blob data specified a size larger than the remaining message data")
} }
blobLengthPadding := 4 - (blobLength % 4) blobLengthPadding := 4 - (blobLength % 4)
@@ -336,7 +249,7 @@ func readOSCBlob(bytes []byte) ([]byte, []byte, error) {
func readOSCColor(bytes []byte) (OSCColor, []byte, error) { func readOSCColor(bytes []byte) (OSCColor, []byte, error) {
if len(bytes) < 4 { if len(bytes) < 4 {
return OSCColor{0, 0, 0, 0}, bytes, errors.New("OSC color arg is not 4 bytes") return OSCColor{0, 0, 0, 0}, bytes, errors.New("color data must be at least 4 bytes large")
} }
oscColor := OSCColor{ oscColor := OSCColor{
r: bytes[0], r: bytes[0],
@@ -346,15 +259,14 @@ func readOSCColor(bytes []byte) (OSCColor, []byte, error) {
} }
return oscColor, bytes[4:], nil return oscColor, bytes[4:], nil
} }
func readOSCTimeTag(bytes []byte) (OSCTimeTag, []byte, error) { func readOSCTimeTag(bytes []byte) (OSCTimeTag, []byte, error) {
seconds, bytesAfterSeconds, err := readOSCInt32(bytes) seconds, bytesAfterSeconds, err := readOSCInt32(bytes)
if err != nil { if err != nil {
return OSCTimeTag{}, bytes, fmt.Errorf("OSC time tag seconds are not valid: %s", err) return OSCTimeTag{}, bytes, err
} }
fractionalSeconds, remainingBytes, err := readOSCInt32(bytesAfterSeconds) fractionalSeconds, remainingBytes, err := readOSCInt32(bytesAfterSeconds)
if err != nil { if err != nil {
return OSCTimeTag{}, bytes, fmt.Errorf("OSC time tag fractional seconds are not valid: %s", err) return OSCTimeTag{}, bytes, err
} }
return OSCTimeTag{ return OSCTimeTag{
@@ -375,30 +287,27 @@ func readOSCArg(bytes []byte, oscType string) (OSCArg, []byte, error) {
//TODO(jwetzell): add error handling //TODO(jwetzell): add error handling
switch oscType { switch oscType {
case "s": case "s":
argString, bytesLeft, err := readOSCString(bytes) argString, bytesLeft := readOSCString(bytes)
if err != nil {
return OSCArg{}, bytes, err
}
oscArg.Value = argString oscArg.Value = argString
remainingBytes = bytesLeft remainingBytes = bytesLeft
case "i": case "i":
argInt, bytesLeft, err := readOSCInt32(bytes) argInt, bytesLeft, error := readOSCInt32(bytes)
if err != nil { if error != nil {
readArgError = err readArgError = error
} }
oscArg.Value = argInt oscArg.Value = argInt
remainingBytes = bytesLeft remainingBytes = bytesLeft
case "f": case "f":
argFloat, bytesLeft, err := readOSCFloat32(bytes) argFloat, bytesLeft, error := readOSCFloat32(bytes)
if err != nil { if error != nil {
readArgError = err readArgError = error
} }
oscArg.Value = argFloat oscArg.Value = argFloat
remainingBytes = bytesLeft remainingBytes = bytesLeft
case "b": case "b":
argBytes, bytesLeft, err := readOSCBlob(bytes) argBytes, bytesLeft, error := readOSCBlob(bytes)
if err != nil { if error != nil {
readArgError = err readArgError = error
} }
oscArg.Value = argBytes oscArg.Value = argBytes
remainingBytes = bytesLeft remainingBytes = bytesLeft
@@ -415,36 +324,29 @@ func readOSCArg(bytes []byte, oscType string) (OSCArg, []byte, error) {
oscArg.Value = math.MaxInt32 oscArg.Value = math.MaxInt32
remainingBytes = bytes remainingBytes = bytes
case "r": case "r":
argColor, bytesLeft, err := readOSCColor(bytes) argColor, bytesLeft, error := readOSCColor(bytes)
if err != nil { if error != nil {
readArgError = err readArgError = error
} }
oscArg.Value = argColor oscArg.Value = argColor
remainingBytes = bytesLeft remainingBytes = bytesLeft
case "h": case "h":
argInt, bytesLeft, err := readOSCInt64(bytes) argInt, bytesLeft, error := readOSCInt64(bytes)
if err != nil { if error != nil {
readArgError = err readArgError = error
} }
oscArg.Value = argInt oscArg.Value = argInt
remainingBytes = bytesLeft remainingBytes = bytesLeft
case "d": case "d":
argFloat, bytesLeft, err := readOSCFloat64(bytes) argFloat, bytesLeft, error := readOSCFloat64(bytes)
if err != nil { if error != nil {
readArgError = err readArgError = error
} }
oscArg.Value = argFloat oscArg.Value = argFloat
remainingBytes = bytesLeft remainingBytes = bytesLeft
case "t":
argTimeTag, bytesLeft, err := readOSCTimeTag(bytes)
if err != nil {
readArgError = err
}
oscArg.Value = argTimeTag
remainingBytes = bytesLeft
default: default:
fmt.Printf("unsupported osc type: %s\n", oscType) fmt.Printf("unsupported osc type: %s\n", oscType)
readArgError = errors.New("unsupported OSC argument type: " + oscType) readArgError = errors.New("unsupported osc type: " + oscType)
} }
return oscArg, remainingBytes, readArgError return oscArg, remainingBytes, readArgError
} }
-318
View File
@@ -1,318 +0,0 @@
package osc
import (
"reflect"
"testing"
)
func TestGoodOSCArgsToBuffer(t *testing.T) {
testCases := []struct {
name string
args []OSCArg
expected []byte
}{
{
name: "int arg",
args: []OSCArg{
{
Type: "i",
Value: int(123),
},
},
expected: []byte{0, 0, 0, 123},
},
{
name: "int32 arg",
args: []OSCArg{
{
Type: "i",
Value: int32(123),
},
},
expected: []byte{0, 0, 0, 123},
},
{
name: "float32 arg",
args: []OSCArg{
{
Type: "f",
Value: float32(123),
},
},
expected: []byte{66, 246, 0, 0},
},
{
name: "float32 arg with int value",
args: []OSCArg{
{
Type: "f",
Value: int(123),
},
},
expected: []byte{66, 246, 0, 0},
},
{
name: "float32 arg with int32 value",
args: []OSCArg{
{
Type: "f",
Value: int32(123),
},
},
expected: []byte{66, 246, 0, 0},
},
{
name: "float32 arg with int64 value",
args: []OSCArg{
{
Type: "f",
Value: int64(123),
},
},
expected: []byte{66, 246, 0, 0},
},
{
name: "float64 arg",
args: []OSCArg{
{
Type: "d",
Value: float64(123),
},
},
expected: []byte{64, 94, 192, 0, 0, 0, 0, 0},
},
{
name: "float64 arg with float32 value",
args: []OSCArg{
{
Type: "d",
Value: float32(123),
},
},
expected: []byte{64, 94, 192, 0, 0, 0, 0, 0},
},
{
name: "float64 arg with int value",
args: []OSCArg{
{
Type: "d",
Value: int(123),
},
},
expected: []byte{64, 94, 192, 0, 0, 0, 0, 0},
},
{
name: "float64 arg with int32 value",
args: []OSCArg{
{
Type: "d",
Value: int32(123),
},
},
expected: []byte{64, 94, 192, 0, 0, 0, 0, 0},
},
{
name: "float64 arg with int64 value",
args: []OSCArg{
{
Type: "d",
Value: int64(123),
},
},
expected: []byte{64, 94, 192, 0, 0, 0, 0, 0},
},
{
name: "int64 arg",
args: []OSCArg{
{
Type: "h",
Value: int64(123),
},
},
expected: []byte{0, 0, 0, 0, 0, 0, 0, 123},
},
{
name: "int64 arg with int32 value",
args: []OSCArg{
{
Type: "h",
Value: int32(123),
},
},
expected: []byte{0, 0, 0, 0, 0, 0, 0, 123},
},
{
name: "blob arg",
args: []OSCArg{
{
Type: "b",
Value: []byte{1, 2, 3},
},
},
expected: []byte{0, 0, 0, 3, 1, 2, 3, 0},
},
{
name: "true arg",
args: []OSCArg{
{
Type: "T",
Value: true,
},
},
expected: []byte{},
},
{
name: "false arg",
args: []OSCArg{
{
Type: "F",
Value: false,
},
},
expected: []byte{},
},
{
name: "nil arg",
args: []OSCArg{
{
Type: "N",
Value: nil,
},
},
expected: []byte{},
},
{
name: "inifinitum arg",
args: []OSCArg{
{
Type: "I",
Value: nil,
},
},
expected: []byte{},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
got, err := argsToBuffer(testCase.args)
if err != nil {
t.Fatalf("failed to encode properly: %s", err.Error())
}
if !reflect.DeepEqual(got, testCase.expected) {
t.Fatalf("failed to encode properly got '%v', expected '%v'", got, testCase.expected)
}
})
}
}
func TestBadOSCArgsToBuffer(t *testing.T) {
testCases := []struct {
name string
args []OSCArg
errorString string
}{}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
got, err := argsToBuffer(testCase.args)
if err == nil {
t.Fatalf("argsToBuffer expected to fail but got: %+v", got)
}
if err.Error() != testCase.errorString {
t.Fatalf("argsToBuffer got error '%s', expected '%s'", err.Error(), testCase.errorString)
}
})
}
}
func TestGoodPacketFromBytes(t *testing.T) {
testCases := []struct {
name string
expected OSCPacket
bytes []byte
}{
{
name: "message with no args",
expected: &OSCMessage{
Address: "/hello",
Args: []OSCArg{},
},
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0},
},
{
name: "bundle with one message with no args",
expected: &OSCBundle{
TimeTag: OSCTimeTag{
seconds: 32,
fractionalSeconds: 0,
},
Contents: []OSCPacket{&OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: float32(440)}}}},
},
bytes: []byte{35, 98, 117, 110, 100, 108, 101, 0, 0, 0, 0,
32, 0, 0, 0, 0, 0, 0, 0, 32, 47, 111,
115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52,
47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0,
44, 102, 0, 0, 67, 220, 0, 0},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
got, remainingBytes, err := PacketFromBytes(testCase.bytes)
if err != nil {
t.Fatalf("failed to decode properly: %s", err.Error())
}
if len(remainingBytes) != 0 {
t.Fatalf("failed to decode properly, expected no remaining bytes but got: %v", remainingBytes)
}
if !reflect.DeepEqual(got, testCase.expected) {
t.Fatalf("failed to decode properly got '%v', expected '%v'", got, testCase.expected)
}
})
}
}
func TestBadPacketFromBytes(t *testing.T) {
testCases := []struct {
name string
bytes []byte
errorString string
}{
{name: "empty bytes",
bytes: []byte{},
errorString: "cannot create OSC Packet from empty byte array",
},
{name: "packet that does not start with / or #",
bytes: []byte{0, 1, 2, 3},
errorString: "OSC Packet must start with # for bundle or / for message",
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
got, _, err := PacketFromBytes(testCase.bytes)
if err == nil {
t.Fatalf("PacketFromBytes expected to fail but got: %+v", got)
}
if err.Error() != testCase.errorString {
t.Fatalf("PacketFromBytes got error '%s', expected '%s'", err.Error(), testCase.errorString)
}
})
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
package osc package osc
type OSCPacket interface { type OSCPacket interface {
ToBytes() ([]byte, error) ToBytes() []byte
} }
type OSCBundle struct { type OSCBundle struct {