#include "../conversion/binaryconversion.h" #include "../conversion/stringconversion.h" #include "../tests/testutils.h" #include #include #include #include #include #include using namespace std; using namespace ConversionUtilities; using namespace TestUtilities; using namespace CPPUNIT_NS; /*! * \brief The ConversionTests class tests classes and methods of the ConversionUtilities namespace. */ class ConversionTests : public TestFixture { CPPUNIT_TEST_SUITE(ConversionTests); CPPUNIT_TEST(testEndianness); CPPUNIT_TEST(testBinaryConversions); CPPUNIT_TEST(testSwapOrderFunctions); CPPUNIT_TEST(testStringEncodingConversions); CPPUNIT_TEST(testStringConversions); CPPUNIT_TEST_SUITE_END(); public: ConversionTests(); void setUp() {} void tearDown() {} void testEndianness(); void testBinaryConversions(); void testSwapOrderFunctions(); void testStringEncodingConversions(); void testStringConversions(); private: template void testConversion(const char *message, function vice, function verca, intType min, intType max); char m_buff[8]; random_device m_randomDevice; mt19937 m_randomEngine; }; CPPUNIT_TEST_SUITE_REGISTRATION(ConversionTests); ConversionTests::ConversionTests() : m_randomDevice(), m_randomEngine(m_randomDevice()) {} /*! * \brief Tests whether macros for endianness are correct. */ void ConversionTests::testEndianness() { union { uint32_t integer; char characters[4]; } test = {0x01020304}; #if defined(CONVERSION_UTILITIES_BYTE_ORDER_BIG_ENDIAN) // test whether macro definitions are consistent CPPUNIT_ASSERT(CONVERSION_UTILITIES_IS_BYTE_ORDER_BIG_ENDIAN == true); CPPUNIT_ASSERT(CONVERSION_UTILITIES_IS_BYTE_ORDER_LITTLE_ENDIAN == false); // test whether byte order assumption is correct CPPUNIT_ASSERT_MESSAGE("Byte order assumption (big-endian) is wrong", test.characters[0] == 0x01); #elif defined(CONVERSION_UTILITIES_BYTE_ORDER_LITTLE_ENDIAN) // test whether macro definitions are consistent CPPUNIT_ASSERT(CONVERSION_UTILITIES_IS_BYTE_ORDER_BIG_ENDIAN == false); CPPUNIT_ASSERT(CONVERSION_UTILITIES_IS_BYTE_ORDER_LITTLE_ENDIAN == true); // test whether byte order assumption is correct CPPUNIT_ASSERT_MESSAGE("Byte order assumption (little-endian) is wrong", test.characters[0] == 0x04); #else CPPUNIT_FAIL("There is not valid byte order assumption"); #endif } template void ConversionTests::testConversion(const char *message, function vice, function versa, intType min, intType max) { const intType random = uniform_int_distribution(min, max)(m_randomEngine); stringstream msg; msg << message << '(' << hex << '0' << 'x' << random << ')'; vice(random, m_buff); CPPUNIT_ASSERT_MESSAGE(msg.str(), versa(m_buff) == random); } #define TEST_TYPE(endianness, function) \ decltype(endianness::function(m_buff)) #define TEST_CONVERSION(function, endianness) \ testConversion( \ "testing " #function, \ static_cast(&endianness::getBytes), \ endianness::function, \ numeric_limits::min(), \ numeric_limits::max() \ ) #define TEST_BE_CONVERSION(function) \ TEST_CONVERSION( \ function, BE \ ) #define TEST_LE_CONVERSION(function) \ TEST_CONVERSION( \ function, LE \ ) #define TEST_CUSTOM_CONVERSION(vice, versa, endianness, min, max) \ testConversion( \ "testing " #versa, \ static_cast(&endianness::vice), \ endianness::versa, \ min, max \ ) /*! * \brief Tests most important binary conversions. * * Tests toUInt16(), ... toUInt64(), toInt16(), ... toInt64() and * the inverse getBytes() functions with random numbers. */ void ConversionTests::testBinaryConversions() { // test to...() / getBytes() with random numbers for(byte b = 1; b < 100; ++b) { TEST_BE_CONVERSION(toUInt16); TEST_BE_CONVERSION(toUInt32); TEST_BE_CONVERSION(toUInt64); TEST_LE_CONVERSION(toUInt16); TEST_LE_CONVERSION(toUInt32); TEST_LE_CONVERSION(toUInt64); TEST_BE_CONVERSION(toInt16); TEST_BE_CONVERSION(toInt32); TEST_BE_CONVERSION(toInt64); TEST_LE_CONVERSION(toInt16); TEST_LE_CONVERSION(toInt32); TEST_LE_CONVERSION(toInt64); TEST_CUSTOM_CONVERSION(getBytes24, toUInt24, BE, 0, 0xFFFFFF); TEST_CUSTOM_CONVERSION(getBytes24, toUInt24, LE, 0, 0xFFFFFF); } } /*! * \brief Tests swap order functions. */ void ConversionTests::testSwapOrderFunctions() { CPPUNIT_ASSERT(swapOrder(static_cast(0x7825)) == 0x2578); CPPUNIT_ASSERT(swapOrder(static_cast(0x12345678)) == 0x78563412); CPPUNIT_ASSERT(swapOrder(static_cast(0x1122334455667788)) == 0x8877665544332211); } /*! * \brief Internally used for string encoding tests to check results. */ void assertEqual(const char *message, const byte *expectedValues, size_t expectedSize, const pair, size_t> &actualValues) { // check whether number of elements matches CPPUNIT_ASSERT_EQUAL_MESSAGE(message, expectedSize, actualValues.second); // check whether contents match auto *end = expectedValues + expectedSize; auto *i = reinterpret_cast(actualValues.first.get()); for(; expectedValues != end; ++expectedValues, ++i) { CPPUNIT_ASSERT_EQUAL_MESSAGE(message, asHexNumber(*expectedValues), asHexNumber(*i)); } } #if CONVERSION_UTILITIES_IS_BYTE_ORDER_LITTLE_ENDIAN == true # define LE_STR_FOR_ENDIANNESS(name) name ## LE ## String # define BE_STR_FOR_ENDIANNESS(name) name ## BE ## String #elif CONVERSION_UTILITIES_IS_BYTE_ORDER_BIG_ENDIAN == true # define LE_STR_FOR_ENDIANNESS(name) name ## BE ## String # define BE_STR_FOR_ENDIANNESS(name) name ## LE ## String #endif /*! * \def LE_STR_FOR_ENDIANNESS * \brief Selects right string for little-endian checks. */ /*! * \def BE_STR_FOR_ENDIANNESS * \brief Selects right string for big-endian checks. */ /*! * \brief Tests string encoding conversions. */ void ConversionTests::testStringEncodingConversions() { // define test string "ABCD" for the different encodings const byte simpleString[] = {'A', 'B', 'C', 'D'}; const uint16 simpleUtf16LEString[] = {0x0041, 0x0042, 0x0043, 0x0044}; const uint16 simpleUtf16BEString[] = {0x4100, 0x4200, 0x4300, 0x4400}; // define test string "ABĂ–CD" for the different encodings const byte latin1String[] = {'A', 'B', 0xD6, 'C', 'D'}; const byte utf8String[] = {'A', 'B', 0xC3, 0x96, 'C', 'D'}; const uint16 utf16LEString[] = {0x0041, 0x0042, 0x00D6, 0x0043, 0x0044}; const uint16 utf16BEString[] = {0x4100, 0x4200, 0xD600, 0x4300, 0x4400}; // test conversion to UTF-8 assertEqual("Latin-1 to UTF-8 (simple)", simpleString, 4, convertLatin1ToUtf8(reinterpret_cast(simpleString), 4)); assertEqual("Latin-1 to UTF-8", utf8String, 6, convertLatin1ToUtf8(reinterpret_cast(latin1String), 5)); assertEqual("UTF-16LE to UTF-8 (simple)", simpleString, 4, convertUtf16LEToUtf8(reinterpret_cast(LE_STR_FOR_ENDIANNESS(simpleUtf16)), 8)); assertEqual("UTF-16LE to UTF-8", utf8String, 6, convertUtf16LEToUtf8(reinterpret_cast(LE_STR_FOR_ENDIANNESS(utf16)), 10)); assertEqual("UTF-16BE to UTF-8 (simple)", simpleString, 4, convertUtf16BEToUtf8(reinterpret_cast(BE_STR_FOR_ENDIANNESS(simpleUtf16)), 8)); assertEqual("UTF-16BE to UTF-8", utf8String, 6, convertUtf16BEToUtf8(reinterpret_cast(BE_STR_FOR_ENDIANNESS(utf16)), 10)); // test conversion from UTF-8 assertEqual("UTF-8 to Latin-1 (simple)", simpleString, 4, convertUtf8ToLatin1(reinterpret_cast(simpleString), 4)); assertEqual("UTF-8 to Latin-1", latin1String, 5, convertUtf8ToLatin1(reinterpret_cast(utf8String), 6)); assertEqual("UTF-8 to UFT-16LE (simple)", reinterpret_cast(LE_STR_FOR_ENDIANNESS(simpleUtf16)), 8, convertUtf8ToUtf16LE(reinterpret_cast(simpleString), 4)); assertEqual("UTF-8 to UFT-16LE", reinterpret_cast(LE_STR_FOR_ENDIANNESS(utf16)), 10, convertUtf8ToUtf16LE(reinterpret_cast(utf8String), 6)); assertEqual("UTF-8 to UFT-16BE (simple)", reinterpret_cast(BE_STR_FOR_ENDIANNESS(simpleUtf16)), 8, convertUtf8ToUtf16BE(reinterpret_cast(simpleString), 4)); assertEqual("UTF-8 to UFT-16BE", reinterpret_cast(BE_STR_FOR_ENDIANNESS(utf16)), 10, convertUtf8ToUtf16BE(reinterpret_cast(utf8String), 6)); } /*! * \brief Tests miscellaneous string conversions. */ void ConversionTests::testStringConversions() { // test stringToNumber() / numberToString() with random numbers uniform_int_distribution randomDistSigned(numeric_limits::min()); uniform_int_distribution randomDistUnsigned(0); for(byte b = 1; b < 100; ++b) { auto signedRandom = randomDistSigned(m_randomEngine); auto unsignedRandom = randomDistUnsigned(m_randomEngine); for(const auto base : initializer_list{2, 8, 10, 16}) { auto resultString = stringToNumber(numberToString(unsignedRandom, base), base); auto resultWideString = stringToNumber(numberToString(unsignedRandom, base), base); CPPUNIT_ASSERT(resultString == unsignedRandom); CPPUNIT_ASSERT(resultWideString == unsignedRandom); } for(const auto base : initializer_list{10}) { auto resultString = stringToNumber(numberToString(signedRandom, base), base); auto resultWideString = stringToNumber(numberToString(signedRandom, base), base); CPPUNIT_ASSERT(resultString == signedRandom); CPPUNIT_ASSERT(resultWideString == signedRandom); } } // test interpretIntegerAsString() CPPUNIT_ASSERT(interpretIntegerAsString(0x54455354) == "TEST"); // test splitString() / joinStrings() auto splitJoinTest = joinStrings(splitString >(",a,,ab,ABC,s", ",", EmptyPartsTreat::Keep), " ", false, "(", ")"); CPPUNIT_ASSERT(splitJoinTest == "() (a) () (ab) (ABC) (s)"); splitJoinTest = joinStrings(splitString >(",a,,ab,ABC,s", ",", EmptyPartsTreat::Keep), " ", true, "(", ")"); CPPUNIT_ASSERT(splitJoinTest == "(a) (ab) (ABC) (s)"); splitJoinTest = joinStrings(splitString >(",a,,ab,ABC,s", ",", EmptyPartsTreat::Omit), " ", false, "(", ")"); CPPUNIT_ASSERT(splitJoinTest == "(a) (ab) (ABC) (s)"); splitJoinTest = joinStrings(splitString >(",a,,ab,ABC,s", ",", EmptyPartsTreat::Merge), " ", false, "(", ")"); CPPUNIT_ASSERT(splitJoinTest == "(a,ab) (ABC) (s)"); // test findAndReplace() string findReplaceTest("findAndReplace()"); findAndReplace(findReplaceTest, "And", "Or"); CPPUNIT_ASSERT(findReplaceTest == "findOrReplace()"); // test startsWith() CPPUNIT_ASSERT(!startsWith(findReplaceTest, "findAnd")); CPPUNIT_ASSERT(startsWith(findReplaceTest, "findOr")); // test encodeBase64() / decodeBase64() with random data uniform_int_distribution randomDistChar; byte originalBase64Data[4047]; for(byte &c : originalBase64Data) { c = randomDistChar(m_randomEngine); } const auto encodedBase64Data = encodeBase64(originalBase64Data, sizeof(originalBase64Data)); auto decodedBase64Data = decodeBase64(encodedBase64Data.data(), encodedBase64Data.size()); CPPUNIT_ASSERT(decodedBase64Data.second == sizeof(originalBase64Data)); for(unsigned int i = 0; i < sizeof(originalBase64Data); ++i) { CPPUNIT_ASSERT(decodedBase64Data.first[i] == originalBase64Data[i]); } }