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
14 changed files with 265 additions and 1102 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
coverage*
build
+10 -25
View File
@@ -4,51 +4,36 @@ import (
"errors"
)
func (b *OSCBundle) ToBytes() ([]byte, error) {
func (b *OSCBundle) ToBytes() []byte {
bytes := stringToOSCBytes("#bundle")
timeTagBytes, err := timeTagToOSCBytes(b.TimeTag)
if err != nil {
return nil, err
}
bytes = append(bytes, timeTagBytes...)
bytes = append(bytes, timeTagToOSCBytes(b.TimeTag)...)
for _, packet := range b.Contents {
packetBytes, err := packet.ToBytes()
if err != nil {
return nil, err
}
packetLength := len(packetBytes)
packetBytes := packet.ToBytes()
packetLength := len(packet.ToBytes())
packetLengthBytes, err := int32ToOSCBytes(int32(packetLength))
if err != nil {
return nil, err
}
bytes = append(bytes, packetLengthBytes...)
bytes = append(bytes, int32ToOSCBytes(int32(packetLength))...)
bytes = append(bytes, packetBytes...)
}
return bytes, nil
return bytes
}
func BundleFromBytes(bytes []byte) (*OSCBundle, []byte, error) {
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 {
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)
if err != nil {
return nil, bytes, err
}
bundleHeader, bytesAfterBundleHeader := readOSCString(bytes)
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)
+49 -172
View File
@@ -1,212 +1,89 @@
package osc
import (
"fmt"
"reflect"
"testing"
)
func TestGoodOSCBundleEncoding(t *testing.T) {
func TestOSCBundleEncoding(t *testing.T) {
testCases := []struct {
name 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
description string
bundle *OSCBundle
errorString string
expected []byte
}{
{
name: "bundle contains message with bad address",
bundle: &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{
"simple contents single message",
&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,
[]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 {
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",
expected: &OSCBundle{
"simple contents single message",
&OSCBundle{
TimeTag: OSCTimeTag{
seconds: 32,
fractionalSeconds: 0,
},
Contents: []OSCPacket{&OSCBundle{
TimeTag: OSCTimeTag{
seconds: 64,
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, // #bundle
0, 0, 0, 32, 0, 0, 0, 0, // time tag
0, 0, 0, 52, // content size
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,
[]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) {
actual, remainingBytes, error := BundleFromBytes(testCase.bytes)
if error != nil {
t.Fatalf("failed to decode properly: %s", error.Error())
}
actual, remainingBytes, error := BundleFromBytes(testCase.bytes)
if len(remainingBytes) > 0 {
t.Fatalf("should not have any remaining bytes")
}
if error != nil {
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))
}
oscMessageBuffer, err := oscMessage.ToBytes()
if err != nil {
panic(err)
}
oscMessageBuffer := oscMessage.ToBytes()
if slip {
oscMessageBuffer = slipEncode(oscMessageBuffer)
+5 -6
View File
@@ -88,7 +88,7 @@ func main() {
func listenTCP(netAddress string, useSLIP bool, format string) {
socket, err := net.Listen("tcp4", netAddress)
if err != nil {
fmt.Println(err)
// TODO(jwetzell): output error properly
return
}
@@ -97,7 +97,7 @@ func listenTCP(netAddress string, useSLIP bool, format string) {
for {
conn, err := socket.Accept()
if err != nil {
fmt.Println(err)
// TODO(jwetzell): output error properly
continue
}
go handleTCPConnection(conn, useSLIP, format)
@@ -185,9 +185,8 @@ func handlePacket(message osc.OSCPacket, format string) {
handleBundle(bundle, format)
} else if msg, ok := message.(*osc.OSCMessage); ok {
handleMessage(msg, format)
} else {
fmt.Println("Received unknown OSC Packet type")
}
// TODO(jwetzell): handle other packet types?
}
func handleMessage(message *osc.OSCMessage, format string) {
@@ -209,13 +208,13 @@ func listenUDP(netAddress string, format string) {
s, err := net.ResolveUDPAddr("udp4", netAddress)
if err != nil {
fmt.Println(err)
// TODO(jwetzell): output error properly
return
}
connection, err := net.ListenUDP("udp4", s)
if err != nil {
fmt.Println(err)
// TODO(jwetzell): output error properly
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()
if err != nil {
panic(err)
}
oscMessageBuffer := oscMessage.ToBytes()
if slip {
oscMessageBuffer = slipEncode(oscMessageBuffer)
+1 -1
View File
@@ -2,4 +2,4 @@ module github.com/jwetzell/osc-go
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/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
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.8.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/urfave/cli/v3 v3.6.2 h1:lQuqiPrZ1cIz8hz+HcrG0TNZFxU70dPZ3Yl+pSrH9A8=
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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+13 -36
View File
@@ -5,16 +5,8 @@ import (
"strings"
)
func (m *OSCMessage) ToBytes() ([]byte, error) {
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 /")
}
func (m *OSCMessage) ToBytes() []byte {
//TODO(jwetzell): add error handling
oscBuffer := []byte{}
oscBuffer = append(oscBuffer, stringToOSCBytes(m.Address)...)
@@ -26,28 +18,22 @@ func (m *OSCMessage) ToBytes() ([]byte, error) {
for _, arg := range m.Args {
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) {
if len(bytes) == 0 {
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 {
return nil, err
if address[0] != 47 {
return nil, errors.New("OSC Message address must start with /")
}
oscMessage := OSCMessage{
@@ -55,16 +41,7 @@ func MessageFromBytes(bytes []byte) (*OSCMessage, error) {
Args: []OSCArg{},
}
if len(typeAndArgBytes) == 0 {
// NOTE(jwetzell): no type string return early.
return &oscMessage, nil
}
typeString, argBytes, err := readOSCString(typeAndArgBytes)
if err != nil {
return nil, err
}
typeString, argBytes := readOSCString(typeAndArgBytes)
for index, oscType := range typeString {
if index == 0 {
@@ -72,9 +49,9 @@ func MessageFromBytes(bytes []byte) (*OSCMessage, error) {
return nil, errors.New("type string is malformed")
}
} else {
oscArg, remainingBytes, err := readOSCArg(argBytes, string(oscType))
if err != nil {
return nil, err
oscArg, remainingBytes, error := readOSCArg(argBytes, string(oscType))
if error != nil {
return nil, error
}
argBytes = remainingBytes
oscMessage.Args = append(oscMessage.Args, oscArg)
+105 -333
View File
@@ -1,29 +1,30 @@
package osc
import (
"fmt"
"math"
"reflect"
"testing"
)
func TestGoodOSCMessageEncoding(t *testing.T) {
func TestOSCMessageEncoding(t *testing.T) {
testCases := []struct {
name string
message *OSCMessage
expected []byte
description string
message *OSCMessage
expected []byte
}{
{
name: "simple hello",
message: &OSCMessage{
"simple hello",
&OSCMessage{
Address: "/hello",
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",
message: &OSCMessage{
"simple address string arg",
&OSCMessage{
Address: "/hello",
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",
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},
description: "simple address integer arg",
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},
},
{
name: "simple address float arg",
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},
description: "simple address float arg",
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},
},
{
name: "simple address blob arg",
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},
description: "simple address blob arg",
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},
},
{
name: "simple address True arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "T", Value: true}}},
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 84, 0, 0},
description: "simple address True arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "T", Value: true}}},
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 84, 0, 0},
},
{
name: "simple address False arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "F", Value: false}}},
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 70, 0, 0},
description: "simple address False arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "F", Value: false}}},
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 70, 0, 0},
},
{
name: "simple address color arg",
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},
description: "simple address color arg",
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},
},
{
name: "simple address nil arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "N", Value: nil}}},
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 78, 0, 0},
description: "simple address nil arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "N", Value: nil}}},
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 78, 0, 0},
},
{
name: "simple address int64 arg",
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},
description: "simple address int64 arg",
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},
},
{
name: "simple address float64 arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "d", Value: 12.7654763}}},
description: "simple address float64 arg",
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "d", Value: 12.7654763}}},
expected: []byte{
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
// {
// name: "simple address array arg",
// description: "simple address array arg",
// message: OSCMessage{
// Address: "/hello",
// Args: []OSCArg{
@@ -99,15 +100,15 @@ func TestGoodOSCMessageEncoding(t *testing.T) {
// },
// },
{
name: "osc 1.0 spec example 1",
message: &OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: 440}}},
description: "osc 1.0 spec example 1",
message: &OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: 440}}},
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,
102, 0, 0, 67, 220, 0, 0,
},
},
{
name: "osc 1.0 spec example 2",
description: "osc 1.0 spec example 2",
message: &OSCMessage{
Address: "/foo",
Args: []OSCArg{
@@ -127,181 +128,81 @@ func TestGoodOSCMessageEncoding(t *testing.T) {
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
got, err := testCase.message.ToBytes()
if err != nil {
t.Fatalf("failed to encode properly: %s", err.Error())
}
actual := testCase.message.ToBytes()
if !reflect.DeepEqual(got, testCase.expected) {
t.Fatalf("failed to encode properly got '%v', expected '%v'", got, testCase.expected)
}
})
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 TestBadOSCMessageEncoding(t *testing.T) {
func TestOSCMessageDecoding(t *testing.T) {
testCases := []struct {
name string
message *OSCMessage
errorString string
description string
bytes []byte
expected OSCMessage
}{
{
name: "empty message",
message: &OSCMessage{},
errorString: "OSC Message must have an address",
description: "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: "address does not start with /",
message: &OSCMessage{Address: "hello"},
errorString: "OSC Message address must start with /",
description: "simple address string arg",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 115, 0, 0, 97, 114, 103, 49, 0, 0, 0, 0},
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "s", Value: "arg1"}}},
},
{
name: "arg with unsupported type",
message: &OSCMessage{
Address: "/hello",
Args: []OSCArg{{Type: "x", Value: "unsupported"}},
},
errorString: "unsupported OSC argument type: x",
description: "simple address integer 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: "string arg that is not a string",
message: &OSCMessage{
Address: "/hello",
Args: []OSCArg{{Type: "s", Value: 123}},
},
errorString: "OSC arg had string type but non-string value",
description: "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: "int32 arg that is not a number",
message: &OSCMessage{
Address: "/hello",
Args: []OSCArg{{Type: "i", Value: "not an int"}},
},
errorString: "OSC arg had int32 type but non-number value",
description: "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: "float32 arg that is not a number",
message: &OSCMessage{
Address: "/hello",
Args: []OSCArg{{Type: "f", Value: "not a float"}},
},
errorString: "OSC arg had float32 type but non-number value",
description: "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: "int64 arg that is not a number",
message: &OSCMessage{
Address: "/hello",
Args: []OSCArg{{Type: "h", Value: "not an int"}},
},
errorString: "OSC arg had int64 type but non-number value",
description: "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: "float64 arg that is not a number",
message: &OSCMessage{
Address: "/hello",
Args: []OSCArg{{Type: "d", Value: "not a float"}},
},
errorString: "OSC arg had float64 type but non-number value",
description: "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: "blob arg that is not a byte array",
message: &OSCMessage{
Address: "/hello",
Args: []OSCArg{{Type: "b", Value: "not a blob"}},
},
errorString: "OSC arg had blob type but non-blob value",
description: "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: "color arg that is not an OSCColor",
message: &OSCMessage{
Address: "/hello",
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{}},
description: "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 string arg",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 115, 0, 0, 97, 114, 103, 49, 0, 0, 0, 0},
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "s", Value: "arg1"}}},
description: "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 integer 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",
description: "simple address float64 arg",
bytes: []byte{
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
// {
// name: "simple address array arg",
// description: "simple address array arg",
// 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,
// 0, 0, 3, 232,
@@ -325,15 +226,15 @@ func TestGoodOSCMessageDecoding(t *testing.T) {
// },
// },
{
name: "simple address no type string",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0},
description: "simple address no type string",
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0},
expected: OSCMessage{
Address: "/hello",
Args: []OSCArg{},
},
},
{
name: "osc 1.0 spec example 1",
description: "osc 1.0 spec example 1",
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,
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)}}},
},
{
name: "osc 1.0 spec example 2",
description: "osc 1.0 spec example 2",
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,
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 {
t.Run(testCase.name, func(t *testing.T) {
actual, err := MessageFromBytes(testCase.bytes)
actual, error := MessageFromBytes(testCase.bytes)
if err != nil {
t.Fatalf("failed to encode properly: %s", err.Error())
}
if error != nil {
fmt.Println(error)
t.Errorf("Test '%s' failed to encode properly", testCase.description)
}
if !reflect.DeepEqual(actual.Address, testCase.expected.Address) {
t.Fatalf("failed to encode address propertly got '%s', expected '%s'", actual.Address, testCase.expected.Address)
}
if !reflect.DeepEqual(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) {
t.Fatalf("failed to encode args properly got '%+v', expected '%+v'", actual.Args, testCase.expected.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)
}
})
if !reflect.DeepEqual(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)
}
}
}
+76 -174
View File
@@ -26,51 +26,47 @@ func stringToOSCBytes(rawString string) []byte {
return []byte(sb.String())
}
func int32ToOSCBytes(number int32) ([]byte, error) {
func int32ToOSCBytes(number int32) []byte {
var buf bytes.Buffer
err := binary.Write(&buf, binary.BigEndian, number)
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
err := binary.Write(&buf, binary.BigEndian, number)
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
err := binary.Write(&buf, binary.BigEndian, number)
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
err := binary.Write(&buf, binary.BigEndian, number)
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{}
bytesSize := len(bytes)
bytesSizeBytes, err := int32ToOSCBytes(int32(bytesSize))
if err != nil {
return nil, err
}
oscBytes = append(oscBytes, bytesSizeBytes...)
oscBytes = append(oscBytes, int32ToOSCBytes(int32(bytesSize))...)
oscBytes = append(oscBytes, bytes...)
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) {
timeTagBytes, err := int32ToOSCBytes(timeTag.seconds)
if err != nil {
return nil, err
}
fractionalSecondsBytes, err := int32ToOSCBytes(timeTag.fractionalSeconds)
if err != nil {
return nil, err
}
timeTagBytes = append(timeTagBytes, fractionalSecondsBytes...)
func timeTagToOSCBytes(timeTag OSCTimeTag) []byte {
timeTagBytes := int32ToOSCBytes(timeTag.seconds)
timeTagBytes = append(timeTagBytes, int32ToOSCBytes(timeTag.fractionalSeconds)...)
return timeTagBytes, nil
return timeTagBytes
}
func argsToBuffer(args []OSCArg) ([]byte, error) {
func argsToBuffer(args []OSCArg) []byte {
//TODO(jwetzell): add error handling
var argBuffers = []byte{}
@@ -107,67 +96,35 @@ func argsToBuffer(args []OSCArg) ([]byte, error) {
if value, ok := arg.Value.(string); ok {
argBuffers = append(argBuffers, stringToOSCBytes(value)...)
} 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":
if value, ok := arg.Value.(int); ok {
valueBytes, err := int32ToOSCBytes(int32(value))
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, int32ToOSCBytes(int32(value))...)
} else if value, ok := arg.Value.(int32); ok {
valueBytes, err := int32ToOSCBytes(int32(value))
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, int32ToOSCBytes(value)...)
} 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":
if value, ok := arg.Value.(float32); ok {
valueBytes, err := float32ToOSCBytes(value)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, float32ToOSCBytes(float32(value))...)
} else if value, ok := arg.Value.(float64); ok {
valueBytes, err := float32ToOSCBytes(float32(value))
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, float32ToOSCBytes(float32(value))...)
} else if value, ok := arg.Value.(int); ok {
valueBytes, err := float32ToOSCBytes(float32(value))
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, float32ToOSCBytes(float32(value))...)
} else if value, ok := arg.Value.(int32); ok {
valueBytes, err := float32ToOSCBytes(float32(value))
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, float32ToOSCBytes(float32(value))...)
} else if value, ok := arg.Value.(int64); ok {
valueBytes, err := float32ToOSCBytes(float32(value))
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, float32ToOSCBytes(float32(value))...)
} 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":
if value, ok := arg.Value.([]byte); ok {
valueBytes, err := byteArrayToOSCBytes(value)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, byteArrayToOSCBytes(value)...)
} 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":
argBuffers = append(argBuffers, make([]byte, 0)...)
@@ -179,112 +136,68 @@ func argsToBuffer(args []OSCArg) ([]byte, error) {
argBuffers = append(argBuffers, make([]byte, 0)...)
case "r":
color, ok := arg.Value.(OSCColor)
if !ok {
return nil, errors.New("OSC arg had color type but non-color value")
}
if ok {
colorBytes := []byte{color.r, color.g, color.b, color.a}
argBuffers = append(argBuffers, colorBytes...)
}
case "h":
if value, ok := arg.Value.(int); ok {
valueBytes, err := int64ToOSCBytes(int64(value))
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, int64ToOSCBytes(int64(value))...)
} else if value, ok := arg.Value.(int32); ok {
valueBytes, err := int64ToOSCBytes(int64(value))
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, int64ToOSCBytes(int64(value))...)
} else if value, ok := arg.Value.(int64); ok {
valueBytes, err := int64ToOSCBytes(value)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, int64ToOSCBytes(value)...)
} 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":
if value, ok := arg.Value.(float32); ok {
valueBytes, err := float64ToOSCBytes(float64(value))
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, float64ToOSCBytes(float64(value))...)
} else if value, ok := arg.Value.(float64); ok {
valueBytes, err := float64ToOSCBytes(value)
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, float64ToOSCBytes(float64(value))...)
} else if value, ok := arg.Value.(int); ok {
valueBytes, err := float64ToOSCBytes(float64(value))
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, float64ToOSCBytes(float64(value))...)
} else if value, ok := arg.Value.(int32); ok {
valueBytes, err := float64ToOSCBytes(float64(value))
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, float64ToOSCBytes(float64(value))...)
} else if value, ok := arg.Value.(int64); ok {
valueBytes, err := float64ToOSCBytes(float64(value))
if err != nil {
return nil, err
}
argBuffers = append(argBuffers, valueBytes...)
argBuffers = append(argBuffers, float64ToOSCBytes(float64(value))...)
} 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:
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 := ""
stringEndIndex := 0
nullByteFound := false
for index, byteIn := range bytes {
if byteIn == 0 {
nullByteFound = true
oscString = string(bytes[0:index])
stringEndIndex = index + 1
break
}
}
if !nullByteFound {
return "", bytes, errors.New("OSC string must be null-terminated")
}
stringPadding := 4 - (stringEndIndex % 4)
if stringPadding < 4 {
stringEndIndex = stringEndIndex + stringPadding
}
if stringEndIndex > len(bytes) {
return "", bytes, errors.New("OSC string is not properly padded")
}
remainingBytes := bytes[stringEndIndex:]
return oscString, remainingBytes, nil
return oscString, remainingBytes
}
func readOSCInt32(bytes []byte) (int32, []byte, error) {
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])
return int32(bits), bytes[4:], nil
@@ -292,7 +205,7 @@ func readOSCInt32(bytes []byte) (int32, []byte, error) {
func readOSCInt64(bytes []byte) (int64, []byte, error) {
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])
return int64(bits), bytes[8:], nil
@@ -300,7 +213,7 @@ func readOSCInt64(bytes []byte) (int64, []byte, error) {
func readOSCFloat32(bytes []byte) (float32, []byte, error) {
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])
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) {
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])
return math.Float64frombits(bits), bytes[8:], nil
@@ -318,11 +231,11 @@ func readOSCBlob(bytes []byte) ([]byte, []byte, error) {
blobLength, remainingBytes, err := readOSCInt32(bytes)
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) {
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)
@@ -336,7 +249,7 @@ func readOSCBlob(bytes []byte) ([]byte, []byte, error) {
func readOSCColor(bytes []byte) (OSCColor, []byte, error) {
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{
r: bytes[0],
@@ -346,15 +259,14 @@ func readOSCColor(bytes []byte) (OSCColor, []byte, error) {
}
return oscColor, bytes[4:], nil
}
func readOSCTimeTag(bytes []byte) (OSCTimeTag, []byte, error) {
seconds, bytesAfterSeconds, err := readOSCInt32(bytes)
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)
if err != nil {
return OSCTimeTag{}, bytes, fmt.Errorf("OSC time tag fractional seconds are not valid: %s", err)
return OSCTimeTag{}, bytes, err
}
return OSCTimeTag{
@@ -375,30 +287,27 @@ func readOSCArg(bytes []byte, oscType string) (OSCArg, []byte, error) {
//TODO(jwetzell): add error handling
switch oscType {
case "s":
argString, bytesLeft, err := readOSCString(bytes)
if err != nil {
return OSCArg{}, bytes, err
}
argString, bytesLeft := readOSCString(bytes)
oscArg.Value = argString
remainingBytes = bytesLeft
case "i":
argInt, bytesLeft, err := readOSCInt32(bytes)
if err != nil {
readArgError = err
argInt, bytesLeft, error := readOSCInt32(bytes)
if error != nil {
readArgError = error
}
oscArg.Value = argInt
remainingBytes = bytesLeft
case "f":
argFloat, bytesLeft, err := readOSCFloat32(bytes)
if err != nil {
readArgError = err
argFloat, bytesLeft, error := readOSCFloat32(bytes)
if error != nil {
readArgError = error
}
oscArg.Value = argFloat
remainingBytes = bytesLeft
case "b":
argBytes, bytesLeft, err := readOSCBlob(bytes)
if err != nil {
readArgError = err
argBytes, bytesLeft, error := readOSCBlob(bytes)
if error != nil {
readArgError = error
}
oscArg.Value = argBytes
remainingBytes = bytesLeft
@@ -415,36 +324,29 @@ func readOSCArg(bytes []byte, oscType string) (OSCArg, []byte, error) {
oscArg.Value = math.MaxInt32
remainingBytes = bytes
case "r":
argColor, bytesLeft, err := readOSCColor(bytes)
if err != nil {
readArgError = err
argColor, bytesLeft, error := readOSCColor(bytes)
if error != nil {
readArgError = error
}
oscArg.Value = argColor
remainingBytes = bytesLeft
case "h":
argInt, bytesLeft, err := readOSCInt64(bytes)
if err != nil {
readArgError = err
argInt, bytesLeft, error := readOSCInt64(bytes)
if error != nil {
readArgError = error
}
oscArg.Value = argInt
remainingBytes = bytesLeft
case "d":
argFloat, bytesLeft, err := readOSCFloat64(bytes)
if err != nil {
readArgError = err
argFloat, bytesLeft, error := readOSCFloat64(bytes)
if error != nil {
readArgError = error
}
oscArg.Value = argFloat
remainingBytes = bytesLeft
case "t":
argTimeTag, bytesLeft, err := readOSCTimeTag(bytes)
if err != nil {
readArgError = err
}
oscArg.Value = argTimeTag
remainingBytes = bytesLeft
default:
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
}
-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
type OSCPacket interface {
ToBytes() ([]byte, error)
ToBytes() []byte
}
type OSCBundle struct {