diff --git a/utilities/xmlrpcpp/libb64/src/cdecode.c b/utilities/xmlrpcpp/libb64/src/cdecode.c index a9441e22a7..050ea26faf 100644 --- a/utilities/xmlrpcpp/libb64/src/cdecode.c +++ b/utilities/xmlrpcpp/libb64/src/cdecode.c @@ -28,6 +28,10 @@ int base64_decode_block(const char* code_in, const int length_in, char* plaintex char* plainchar = plaintext_out; char fragment; + if(length_in == 0) { + return 0; + } + *plainchar = state_in->plainchar; switch (state_in->step) @@ -39,7 +43,7 @@ int base64_decode_block(const char* code_in, const int length_in, char* plaintex if (codechar == code_in+length_in) { state_in->step = step_a; - state_in->plainchar = *plainchar; + state_in->plainchar = 0; // no state to save; use default value return plainchar - plaintext_out; } fragment = (char)base64_decode_value(*codechar++); diff --git a/utilities/xmlrpcpp/src/XmlRpcValue.cpp b/utilities/xmlrpcpp/src/XmlRpcValue.cpp index a7c3b0b150..d4eb54d4aa 100644 --- a/utilities/xmlrpcpp/src/XmlRpcValue.cpp +++ b/utilities/xmlrpcpp/src/XmlRpcValue.cpp @@ -478,7 +478,6 @@ namespace XmlRpc { // might reserve too much, we'll shrink later _value.asBinary = new BinaryData(base64DecodedSize(encoded_size), '\0'); - std::stringstream buffer; base64::decoder decoder; std::size_t size = decoder.decode(&valueXml[*offset], encoded_size, &(*_value.asBinary)[0]); _value.asBinary->resize(size); diff --git a/utilities/xmlrpcpp/test/CMakeLists.txt b/utilities/xmlrpcpp/test/CMakeLists.txt index e5ce1ce77f..e0aae5f1af 100644 --- a/utilities/xmlrpcpp/test/CMakeLists.txt +++ b/utilities/xmlrpcpp/test/CMakeLists.txt @@ -3,6 +3,10 @@ if(TARGET xmlrpcvalue_base64) target_link_libraries(xmlrpcvalue_base64 xmlrpcpp) endif() +catkin_add_gtest(test_base64 test_base64.cpp) +target_link_libraries(test_base64 xmlrpcpp) +set_target_properties(test_base64 PROPERTIES COMPILE_FLAGS -std=c++11) + add_library(mock_socket mock_socket.cpp) catkin_add_gtest(test_client diff --git a/utilities/xmlrpcpp/test/test_base64.cpp b/utilities/xmlrpcpp/test/test_base64.cpp new file mode 100644 index 0000000000..b7c38ec0b1 --- /dev/null +++ b/utilities/xmlrpcpp/test/test_base64.cpp @@ -0,0 +1,165 @@ +/* + * Unit tests for XmlRpc++ + * + * Copyright (C) 2017, Zoox Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Austin Hendrix + * + */ + +#include +#include + +#include + +// Test Data for a Base64 encode/decode test +class Base64TestData { +public: + Base64TestData(std::vector raw, std::string encoded) + : raw(raw), encoded(encoded) {} + + std::vector raw; + std::string encoded; +}; + +class Base64Test : public ::testing::TestWithParam {}; + +TEST_P(Base64Test, Encode) { + const std::vector & data = GetParam().raw; + + std::stringstream is; + is.write(&data[0], data.size()); + std::stringstream os; + base64::encoder encoder; + encoder.encode(is, os); + + std::string expected = GetParam().encoded; + + EXPECT_EQ(expected, os.str()); +} + +TEST_P(Base64Test, Decode) { + const std::string& in = GetParam().encoded; + const int encoded_size = in.length(); + + // oversize our output vector (same method used by XmlRpcValue) + std::vector out; + out.resize(encoded_size); + + base64::decoder decoder; + const int size = decoder.decode(in.c_str(), encoded_size, &out[0]); + ASSERT_LE(0, size); + out.resize(size); + + const std::vector & expected = GetParam().raw; + + EXPECT_EQ(expected, out); +} + +INSTANTIATE_TEST_CASE_P( + Multiline, + Base64Test, + ::testing::Values( + Base64TestData({0}, "AA==\n"), + Base64TestData({1, 2}, "AQI=\n"), + Base64TestData({1, 2, 3}, "AQID\n"), + Base64TestData( + // clang-format off + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, -128, -127, -126, -125, -124, -123, -122, -121, + -120, -119, -118, -117, -116, -115, -114, -113, -112, -111, -110, + -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, -99, + -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, -88, -87, -86, + -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, + -72, -71, -70, -69, -68, -67, -66, -65, -64, -63, -62, -61, -60, + -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, + -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, + -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, + -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, + -6, -5, -4, -3, -2, -1}, + // clang-format on + "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMD" + "EyMzQ1\nNjc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYG" + "FiY2RlZmdoaWpr\nbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJ" + "GSk5SVlpeYmZqbnJ2en6Ch\noqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wM" + "HCw8TFxsfIycrLzM3Oz9DR0tPU1dbX\n2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8P" + "Hy8/T19vf4+fr7/P3+/w==\n"))); + +class Base64ErrorData { +public: + // TODO(future work): add error code representation here and check that error + // codes are reported correctly. + Base64ErrorData(std::string encoded) + : encoded(encoded) {} + + std::string encoded; +}; + +class Base64ErrorTest : public ::testing::TestWithParam {}; + +TEST_P(Base64ErrorTest, DecodeErrors) { + const std::string& in = GetParam().encoded; + const int encoded_size = in.length(); + + // oversize our output vector (same method used by XmlRpcValue) + std::vector out; + out.resize(encoded_size); + + base64::decoder decoder; + const int size = decoder.decode(in.c_str(), encoded_size, &out[0]); + // Assert that size is greater or equal to 0, to make sure that the follow-up + // resize will always succeed. + ASSERT_LE(0, size); + out.resize(size); + + // FIXME(future work): decode does not report error on garbage input +} + +INSTANTIATE_TEST_CASE_P( + Multiline, + Base64ErrorTest, + ::testing::Values(// Tests on incomplete data. + Base64ErrorData("="), + Base64ErrorData("A="), + Base64ErrorData("A"), + Base64ErrorData("AA"), + Base64ErrorData("AAA"), + Base64ErrorData("AA="), + // Tests with 4 bytes of good data but which does not + // terminate on the correct boundary. + Base64ErrorData("BBBBA="), + Base64ErrorData("BBBBA"), + Base64ErrorData("BBBBAA"), + Base64ErrorData("BBBBAAA"), + Base64ErrorData("BBBBAA="), + // Decode should succeed and do nothing on empty string. + Base64ErrorData(""), + // Character out of bounds for base64 encoding. + Base64ErrorData("<"))); + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/utilities/xmlrpcpp/test/test_util.cpp b/utilities/xmlrpcpp/test/test_util.cpp index c61da58a6b..d29b2cf6ea 100644 --- a/utilities/xmlrpcpp/test/test_util.cpp +++ b/utilities/xmlrpcpp/test/test_util.cpp @@ -29,7 +29,7 @@ using namespace XmlRpc; class FakeLogHandler : public XmlRpcLogHandler { public: - FakeLogHandler() : last_level(-1), last_msg(NULL){}; + FakeLogHandler() : last_level(-1), last_msg(""){}; virtual void log(int level, const char* msg) { last_level = level; @@ -37,7 +37,7 @@ class FakeLogHandler : public XmlRpcLogHandler { } int last_level; - const char* last_msg; + std::string last_msg; }; TEST(XmlRpc, Log) { @@ -55,7 +55,7 @@ TEST(XmlRpc, Log) { for (int i = 1; i < 6; i++) { XmlRpcUtil::log(i, "Hello"); ASSERT_EQ(-1, fakelog.last_level); - ASSERT_EQ(NULL, fakelog.last_msg); + ASSERT_EQ("", fakelog.last_msg); } // Test masking at levels below maximum verbosity. @@ -65,15 +65,15 @@ TEST(XmlRpc, Log) { for (int j = 1; j <= i; j++) { XmlRpcUtil::log(j, "Hello1"); EXPECT_EQ(j, fakelog.last_level); - EXPECT_STREQ("Hello1", fakelog.last_msg); + EXPECT_EQ("Hello1", fakelog.last_msg); fakelog.last_level = -1; - fakelog.last_msg = NULL; + fakelog.last_msg = ""; } XmlRpcUtil::log(i + 1, "Hello2"); ASSERT_EQ(-1, fakelog.last_level); - ASSERT_EQ(NULL, fakelog.last_msg); + ASSERT_EQ("", fakelog.last_msg); } // Test no messages masked at max verbosity. @@ -81,27 +81,27 @@ TEST(XmlRpc, Log) { for (int i = 1; i < 5; i++) { XmlRpcUtil::log(i, "Hello3"); EXPECT_EQ(i, fakelog.last_level); - EXPECT_STREQ("Hello3", fakelog.last_msg); + EXPECT_EQ("Hello3", fakelog.last_msg); fakelog.last_level = -1; - fakelog.last_msg = NULL; + fakelog.last_msg = ""; } // Basic formatting test. XmlRpcUtil::log(2, "Hello %d", 42); EXPECT_EQ(2, fakelog.last_level); - EXPECT_STREQ("Hello 42", fakelog.last_msg); + EXPECT_EQ("Hello 42", fakelog.last_msg); } class FakeErrorHandler : public XmlRpcErrorHandler { public: - FakeErrorHandler() : last_msg(NULL){}; + FakeErrorHandler() : last_msg(""){}; virtual void error(const char* msg) { last_msg = msg; } - const char* last_msg; + std::string last_msg; }; TEST(XmlRpc, error) { @@ -113,12 +113,12 @@ TEST(XmlRpc, error) { // Basic error check. XmlRpcUtil::error("Error!"); - EXPECT_STREQ("Error!", errors.last_msg); - errors.last_msg = NULL; + EXPECT_EQ("Error!", errors.last_msg); + errors.last_msg = ""; // Error check with formatting. XmlRpcUtil::error("%d: I'm a teapot", 408); - EXPECT_STREQ("408: I'm a teapot", errors.last_msg); + EXPECT_EQ("408: I'm a teapot", errors.last_msg); } int main(int argc, char **argv)