/* test_osc.cpp Copyright (c) 2023 Kevin Matz (kevin.matz@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include /// \cite Spec10 First example OSC-String const uint8_t string1_data[] = { 'O', 'S', 'C', 0x00, // OSC }; /// \cite Spec10 Second example OSC-String const uint8_t string2_data[] = { 'd', 'a', 't', 'a', // data 0x00, 0x00, 0x00, 0x00, }; std::string tag1_string = ",f"; //!< \cite Spec10 First example Type Tag std::string tag2_string = ",iisfff"; //!< \cite Spec10 Second example Type Tag std::string tag3_string = ","; //!< \cite Spec10 Third example Type Tag std::string tag4_string = ",ibb"; //!< \cite Spec10 Fourth example Type Tag std::string address1_string = "/resonators/3/frequency"; //!< \cite Spec10 First example address std::string address2_string = "/a/b/c/d/e"; //!< \cite Spec10 Second example address /// \cite Spec10 First example Message const uint8_t message1_data[] = { 0x2f, 0x6f, 0x73, 0x63, // /osc 0x69, 0x6c, 0x6c, 0x61, // illa 0x74, 0x6f, 0x72, 0x2f, // tor/ 0x34, 0x2f, 0x66, 0x72, // 4/fr 0x65, 0x71, 0x75, 0x65, // eque 0x6e, 0x63, 0x79, 0x00, // ncy 0x2c, 0x66, 0x00, 0x00, // ,f 0x43, 0xdc, 0x00, 0x00, // 440.0 }; /// \cite Spec10 Second example Message const uint8_t message2_data[] = { 0x2f, 0x66, 0x6f, 0x6f, // /foo 0x00, 0x00, 0x00, 0x00, // 0x2c, 0x69, 0x69, 0x73, // ,iis 0x66, 0x66, 0x00, 0x00, // ff 0x00, 0x00, 0x03, 0xe8, // 1000 0xff, 0xff, 0xff, 0xff, // -1 0x68, 0x65, 0x6c, 0x6c, // hell 0x6f, 0x00, 0x00, 0x00, // o 0x3f, 0x9d, 0xf3, 0xb6, // 1.234 0x40, 0xb5, 0xb2, 0x2d, // 5.678 }; TEST(OSC, int_in) { uint8_t value1_data[] = { 0x00, 0x00, 0x03, 0xe8 }; // 1000 uint8_t value2_data[] = { 0xff, 0xff, 0xff, 0xff }; // -1 auto stream1 = std::make_shared(value1_data, sizeof(value1_data)); auto stream2 = std::make_shared(value2_data, sizeof(value2_data)); OSC::int32 value1; OSC::int32 value2; value1.iStream(stream1); value2.iStream(stream2); EXPECT_FALSE(stream1->fail()) << "message 1 stream failed"; EXPECT_FALSE(stream2->fail()) << "message 2 stream failed"; EXPECT_EQ(value1.value, 1000) << "message 1 value mismatch"; EXPECT_EQ(value2.value, -1) << "message 2 value mismatch"; } TEST(OSC, int_out) { uint8_t value1_data[] = { 0x00, 0x00, 0x03, 0xe8 }; // 1000 uint8_t value2_data[] = { 0xff, 0xff, 0xff, 0xff }; // -1 uint8_t buffer1[sizeof(value1_data)] = {0xff}; uint8_t buffer2[sizeof(value2_data)] = {0xff}; auto stream1 = std::make_shared(buffer1, sizeof(buffer1)); auto stream2 = std::make_shared(buffer2, sizeof(buffer2)); OSC::int32 value1(1000); OSC::int32 value2(-1); value1.oStream(stream1); value2.oStream(stream2); EXPECT_FALSE(stream1->fail()) << "value 1 stream failed"; EXPECT_FALSE(stream2->fail()) << "value 2 stream failed"; EXPECT_EQ(value1.streamSize(), sizeof(value1_data)) << "value 1 length mismatch"; EXPECT_EQ(value2.streamSize(), sizeof(value2_data)) << "value 2 length mismatch"; EXPECT_EQ(memcmp(buffer1, value1_data, sizeof(buffer1)), 0) << "value 1 output mismatch"; EXPECT_EQ(memcmp(buffer2, value2_data, sizeof(buffer2)), 0) << "value 2 output mismatch"; } TEST(OSC, float_in) { uint8_t value1_data[] = { 0x3f, 0x9d, 0xf3, 0xb6 }; // 1.234 uint8_t value2_data[] = { 0x40, 0xb5, 0xb2, 0x2d }; // 5.678 auto stream1 = std::make_shared(value1_data, sizeof(value1_data)); auto stream2 = std::make_shared(value2_data, sizeof(value2_data)); OSC::float32 value1; OSC::float32 value2; value1.iStream(stream1); value2.iStream(stream2); EXPECT_FALSE(stream1->fail()) << "message 1 stream failed"; EXPECT_FALSE(stream2->fail()) << "message 2 stream failed"; EXPECT_FLOAT_EQ(value1.value, 1.234) << "message 1 value mismatch"; EXPECT_FLOAT_EQ(value2.value, 5.678) << "message 2 value mismatch"; } TEST(OSC, float_out) { uint8_t value1_data[] = { 0x3f, 0x9d, 0xf3, 0xb6 }; // 1.234 uint8_t value2_data[] = { 0x40, 0xb5, 0xb2, 0x2d }; // 5.678 uint8_t buffer1[sizeof(value1_data)] = {0xff}; uint8_t buffer2[sizeof(value2_data)] = {0xff}; auto stream1 = std::make_shared(buffer1, sizeof(buffer1)); auto stream2 = std::make_shared(buffer2, sizeof(buffer2)); OSC::float32 value1((float)1.234); OSC::float32 value2((float)5.678); value1.oStream(stream1); value2.oStream(stream2); EXPECT_FALSE(stream1->fail()) << "value 1 stream failed"; EXPECT_FALSE(stream2->fail()) << "value 2 stream failed"; EXPECT_EQ(value1.streamSize(), sizeof(value1_data)) << "value 1 length mismatch"; EXPECT_EQ(value2.streamSize(), sizeof(value2_data)) << "value 2 length mismatch"; EXPECT_EQ(memcmp(buffer1, value1_data, sizeof(buffer1)), 0) << "value 1 output mismatch"; EXPECT_EQ(memcmp(buffer2, value2_data, sizeof(buffer2)), 0) << "value 2 output mismatch"; } TEST(OSC, string_in) { EXPECT_FALSE(sizeof(string1_data) %4) << "string_1 data is not 32-bit aligned"; EXPECT_FALSE(sizeof(string2_data) %4) << "string_2 data is not 32-bit aligned"; OSC::string string1; OSC::string string2; uint8_t buffer1[sizeof(string1_data)]; uint8_t buffer2[sizeof(string2_data)]; std::copy(std::begin(string1_data),std::end(string1_data), std::begin(buffer1)); std::copy(std::begin(string2_data),std::end(string2_data), std::begin(buffer2)); auto stream1 = std::make_shared(buffer1, sizeof(buffer1)); auto stream2 = std::make_shared(buffer2, sizeof(buffer2)); string1.iStream(stream1); string2.iStream(stream2); EXPECT_FALSE(stream1->fail()) << "string 1 stream failed"; EXPECT_FALSE(stream2->fail()) << "string 2 stream failed"; EXPECT_EQ(string1.streamSize(), sizeof(string1_data)) << "string_1 data length mismatch"; EXPECT_EQ(string2.streamSize(), sizeof(string2_data)) << "string_2 data length mismatch"; EXPECT_STREQ(string1.value.c_str(), "OSC") << "string_1 value mismatch"; EXPECT_STREQ(string2.value.c_str(), "data") << "string_2 value mismatch"; } TEST(OSC, string_out) { OSC::string string1; OSC::string string2; string1.value = "OSC"; string2.value = "data"; EXPECT_EQ(string1.streamSize(), sizeof(string1_data)) << "string_1 data length mismatch"; EXPECT_EQ(string2.streamSize(), sizeof(string2_data)) << "string_2 data length mismatch"; uint8_t buffer1[sizeof(string1_data)] = {0xff}; uint8_t buffer2[sizeof(string2_data)] = {0xff}; auto stream1 = std::make_shared(buffer1, sizeof(buffer1)); auto stream2 = std::make_shared(buffer2, sizeof(buffer2)); string1.oStream(stream1); string2.oStream(stream2); EXPECT_FALSE(stream1->fail()) << "string 1 stream failed"; EXPECT_FALSE(stream2->fail()) << "string 2 stream failed"; EXPECT_EQ(memcmp(string1_data, buffer1, sizeof(buffer1)), 0) << "string_1 output mismatch"; EXPECT_EQ(memcmp(string2_data, buffer2, sizeof(buffer2)), 0) << "string_2 output mismatch"; } /// \todo Test coverage for OSC Timestamps TEST(OSC, tags_in) { std::string tag5_string = ",f[ii[ss]]"; auto tags1 = OSC::Message::createArguments(tag1_string); auto tags2 = OSC::Message::createArguments(tag2_string); auto tags3 = OSC::Message::createArguments(tag3_string); auto tags4 = OSC::Message::createArguments(tag4_string); auto tags5 = OSC::Message::createArguments(tag5_string); EXPECT_EQ(tags1.size(), 1) << "tag string 1: count mismatch"; EXPECT_EQ(tags2.size(), 6) << "tag string 2: count mismatch"; EXPECT_EQ(tags3.size(), 0) << "tag string 3: count mismatch"; EXPECT_EQ(tags4.size(), 3) << "tag string 4: count mismatch"; EXPECT_STREQ(tags4.back()->type().c_str(), "b") << "tag string 4: blob not matched"; EXPECT_EQ(tags5.size(), 2) << "tag string 5: count mismatch"; EXPECT_STREQ(tags5.back()->type().c_str(), "[ii[ss]]") << "tag string 5: array(s) not matched"; } TEST(OSC, address) { auto *root = new OSC::Method(); EXPECT_FALSE(root->setName("\{}")) << "set invalid method name"; EXPECT_FALSE(root->setName("\[]")) << "set invalid method name"; auto *method1 = new OSC::Method(new OSC::Method(new OSC::Method(root, "resonators"), "3"), "frequency"); auto *method2 = new OSC::Method(new OSC::Method(new OSC::Method(new OSC::Method(new OSC::Method(root, "a"), "b"), "c"), "d"), "e"); EXPECT_STREQ(address1_string.c_str(), method1->address().c_str()) << "address 1 not matched"; EXPECT_STREQ(address2_string.c_str(), method2->address().c_str()) << "address 2 not matched"; delete root; } TEST(OSC, message_in) { EXPECT_FALSE(sizeof(message1_data) %4) << "message_1 data is not 32-bit aligned"; EXPECT_FALSE(sizeof(message2_data) %4) << "message_2 data is not 32-bit aligned"; OSC::Message message1; OSC::Message message2; uint8_t buffer1[sizeof(message1_data)]; uint8_t buffer2[sizeof(message2_data)]; std::copy(std::begin(message1_data),std::end(message1_data), std::begin(buffer1)); std::copy(std::begin(message2_data),std::end(message2_data), std::begin(buffer2)); auto stream1 = std::make_shared(buffer1, sizeof(buffer1)); auto stream2 = std::make_shared(buffer2, sizeof(buffer2)); message1.iStream(stream1); message2.iStream(stream2); EXPECT_FALSE(message1.streamSize() %4) << "Message 1 data size misaligned"; EXPECT_FALSE(message2.streamSize() %4) << "Message 2 data size misaligned"; EXPECT_EQ(message1.streamSize(), sizeof(message1_data)) << "message 1 data size mismatch"; EXPECT_EQ(message2.streamSize(), sizeof(message2_data)) << "message 2 data size mismatch"; EXPECT_FALSE(stream1->fail()) << "message 1 stream failed"; EXPECT_FALSE(stream2->fail()) << "message 2 stream failed"; EXPECT_STREQ(message1.address_pattern.c_str(), "/oscillator/4/frequency") << "message 1: address mismatch"; EXPECT_STREQ(message2.address_pattern.c_str(), "/foo") << "message 2: address mismatch"; EXPECT_STREQ(message1.type_tag().c_str(), ",f") << "message 1: type mismatch"; EXPECT_STREQ(message2.type_tag().c_str(), ",iisff") << "message 2: type mismatch"; auto arg = std::static_pointer_cast(message1.arguments.front()); EXPECT_FLOAT_EQ(arg->value, (float)440.0) << "message 1: value mismatch"; } TEST(OSC, message_out) { OSC::Message message1; message1.address_pattern = "/oscillator/4/frequency"; message1.addArgument((float)440.0); OSC::Message message2; message2.address_pattern = "/foo"; message2.addArgument((int)1000); message2.addArgument((int)-1); message2.addArgument(std::string("hello")); message2.addArgument((float)1.234); message2.addArgument((float)5.678); EXPECT_FALSE(message1.streamSize() %4) << "Message 1 data size misaligned"; EXPECT_FALSE(message2.streamSize() %4) << "Message 2 data size misaligned"; EXPECT_EQ(message1.streamSize(), sizeof(message1_data)) << "message 1 data size mismatch"; EXPECT_EQ(message2.streamSize(), sizeof(message2_data)) << "message 2 data size mismatch"; EXPECT_STREQ(message1.type_tag().c_str(), ",f") << "message 1 type tag mismatch"; EXPECT_STREQ(message2.type_tag().c_str(), ",iisff") << "message 2 type tag mismatch"; uint8_t buffer1[sizeof(message1_data)]; uint8_t buffer2[sizeof(message2_data)]; auto stream1 = std::make_shared(buffer1, sizeof(buffer1)); auto stream2 = std::make_shared(buffer2, sizeof(buffer2)); message1.oStream(stream1); message2.oStream(stream2); EXPECT_FALSE(stream1->fail()) << "message 1 stream failed"; EXPECT_FALSE(stream2->fail()) << "message 2 stream failed"; EXPECT_EQ(memcmp(buffer1, message1_data, sizeof(buffer1)), 0) << "message 1 output mismatch"; EXPECT_EQ(memcmp(buffer2, message2_data, sizeof(buffer2)), 0) << "message 2 output mismatch"; } TEST(OSC, loopback) { auto message1 = std::make_shared(); auto message2 = std::make_shared(); uint8_t buffer1[sizeof(message1_data)]; uint8_t buffer2[sizeof(message2_data)]; std::copy(std::begin(message1_data),std::end(message1_data), std::begin(buffer1)); std::copy(std::begin(message2_data),std::end(message2_data), std::begin(buffer2)); auto stream1 = std::make_shared(buffer1, sizeof(buffer1)); auto stream2 = std::make_shared(buffer2, sizeof(buffer2)); message1->iStream(stream1); message2->iStream(stream2); std::vector hits; auto echo = [&hits](std::shared_ptr msg, const uint8_t *orig) { hits.push_back(msg->address_pattern); auto buffer = std::vector(msg->streamSize()); auto stream = std::make_shared(buffer.data(), buffer.size()); msg->oStream(stream); EXPECT_FALSE(stream->fail()) << "message stream failed"; EXPECT_EQ(memcmp(buffer.data(), orig, buffer.size()), 0) << "message output mismatch"; }; auto echo1 = [&echo](std::shared_ptr msg) { echo(msg, message1_data); }; auto echo2 = [&echo](std::shared_ptr msg) { echo(msg, message2_data); }; OSC::Receiver rx; auto *method1 = new OSC::Method(new OSC::Method(new OSC::Method(rx.rootMethod(), "oscillator"), "4"), "frequency"); auto *method2 = new OSC::Method(rx.rootMethod(), "foo"); auto token1 = method1->onTrigger(echo1); auto token2 = method2->onTrigger(echo2); OSC::Sender tx; auto tokenSend = tx.setSender(std::bind(&OSC::Receiver::rxPacket, &rx, std::placeholders::_1)); hits.clear(); tx.send(message1); ASSERT_EQ(hits.size(), 1) << "method hit mismatch"; EXPECT_STREQ(hits.front().c_str(), "/oscillator/4/frequency") << "method address mismatch"; hits.clear(); tx.send(message2); ASSERT_EQ(hits.size(), 1) << "method hit mismatch"; EXPECT_STREQ(hits.front().c_str(), "/foo") << "method address mismatch"; hits.clear(); auto bundle = std::make_shared(); bundle->elements.push_back(message1); bundle->elements.push_back(message2); tx.send(bundle); ASSERT_EQ(hits.size(), 2) << "bundle hits mismatch"; EXPECT_STREQ(hits.front().c_str(), "/oscillator/4/frequency") << "bundle method 1 address mismatch"; EXPECT_STREQ(hits.back().c_str(), "/foo") << "bundle method 2 address mismatch"; }