diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bc0f89c..d9fc283 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,6 +11,7 @@ if (GTest_FOUND) target_link_libraries(${PROJECT_NAME} PRIVATE GTest::gtest + LCP::OSC LCP::RDM LCP::UUID LCP::sACN diff --git a/test/test_osc.cpp b/test/test_osc.cpp new file mode 100644 index 0000000..3c41ad4 --- /dev/null +++ b/test/test_osc.cpp @@ -0,0 +1,311 @@ +/* + 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 + +/// \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"; +} + + +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"; +}