cpp-utilities/tests/conversiontests.cpp

286 lines
12 KiB
C++
Raw Normal View History

2016-01-25 23:59:52 +01:00
#include "../conversion/binaryconversion.h"
2016-01-27 01:01:43 +01:00
#include "../conversion/stringconversion.h"
#include "../tests/testutils.h"
2016-01-25 23:59:52 +01:00
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestFixture.h>
#include <random>
#include <sstream>
#include <functional>
2016-01-27 01:01:43 +01:00
#include <initializer_list>
2016-01-25 23:59:52 +01:00
using namespace std;
using namespace ConversionUtilities;
using namespace TestUtilities;
2016-01-25 23:59:52 +01:00
using namespace CPPUNIT_NS;
/*!
* \brief The ConversionTests class tests classes and methods of the ConversionUtilities namespace.
*/
2016-01-25 23:59:52 +01:00
class ConversionTests : public TestFixture
{
CPPUNIT_TEST_SUITE(ConversionTests);
CPPUNIT_TEST(testEndianness);
CPPUNIT_TEST(testBinaryConversions);
2016-02-23 19:27:46 +01:00
CPPUNIT_TEST(testSwapOrderFunctions);
CPPUNIT_TEST(testStringEncodingConversions);
2016-01-27 01:01:43 +01:00
CPPUNIT_TEST(testStringConversions);
2016-01-25 23:59:52 +01:00
CPPUNIT_TEST_SUITE_END();
public:
ConversionTests();
void setUp() {}
void tearDown() {}
void testEndianness();
void testBinaryConversions();
2016-02-23 19:27:46 +01:00
void testSwapOrderFunctions();
void testStringEncodingConversions();
2016-01-27 01:01:43 +01:00
void testStringConversions();
2016-01-25 23:59:52 +01:00
private:
template<typename intType>
void testConversion(const char *message, function<void (intType, char *)> vice, function<intType (const char *)> 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)
2016-02-23 19:27:46 +01:00
// 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
2016-01-25 23:59:52 +01:00
CPPUNIT_ASSERT_MESSAGE("Byte order assumption (big-endian) is wrong", test.characters[0] == 0x01);
#elif defined(CONVERSION_UTILITIES_BYTE_ORDER_LITTLE_ENDIAN)
2016-02-23 19:27:46 +01:00
// 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
2016-01-25 23:59:52 +01:00
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<typename intType>
void ConversionTests::testConversion(const char *message, function<void (intType, char *)> vice, function<intType (const char *)> versa, intType min, intType max)
{
const intType random = uniform_int_distribution<intType>(min, max)(m_randomEngine);
stringstream msg;
msg << message << '(' << hex << '0' << 'x' << random << ')';
vice(random, m_buff);
2016-01-27 01:01:43 +01:00
CPPUNIT_ASSERT_MESSAGE(msg.str(), versa(m_buff) == random);
2016-01-25 23:59:52 +01:00
}
#define TEST_TYPE(endianness, function) \
decltype(endianness::function(m_buff))
#define TEST_CONVERSION(function, endianness) \
testConversion<TEST_TYPE(endianness, function)>( \
"testing " #function, \
static_cast<void(*)(TEST_TYPE(endianness, function), char *)>(&endianness::getBytes), \
endianness::function, \
numeric_limits<TEST_TYPE(endianness, function)>::min(), \
numeric_limits<TEST_TYPE(endianness, function)>::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<TEST_TYPE(endianness, versa)>( \
"testing " #versa, \
static_cast<void(*)(TEST_TYPE(endianness, versa), char *)>(&endianness::vice), \
endianness::versa, \
min, max \
)
/*!
2016-01-27 01:01:43 +01:00
* \brief Tests most important binary conversions.
2016-01-25 23:59:52 +01:00
*
* Tests toUInt16(), ... toUInt64(), toInt16(), ... toInt64() and
2016-01-27 01:01:43 +01:00
* the inverse getBytes() functions with random numbers.
2016-01-25 23:59:52 +01:00
*/
void ConversionTests::testBinaryConversions()
{
2016-01-27 01:01:43 +01:00
// test to...() / getBytes() with random numbers
for(byte b = 1; b < 100; ++b) {
2016-01-25 23:59:52 +01:00
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);
2016-01-27 01:01:43 +01:00
TEST_CUSTOM_CONVERSION(getBytes24, toUInt24, BE, 0, 0xFFFFFF);
TEST_CUSTOM_CONVERSION(getBytes24, toUInt24, LE, 0, 0xFFFFFF);
}
}
2016-02-23 19:27:46 +01:00
/*!
* \brief Tests swap order functions.
*/
void ConversionTests::testSwapOrderFunctions()
{
CPPUNIT_ASSERT(swapOrder(static_cast<uint16>(0x7825)) == 0x2578);
CPPUNIT_ASSERT(swapOrder(static_cast<uint32>(0x12345678)) == 0x78563412);
CPPUNIT_ASSERT(swapOrder(static_cast<uint64>(0x1122334455667788)) == 0x8877665544332211);
}
2016-01-27 01:01:43 +01:00
/*!
* \brief Internally used for string encoding tests to check results.
*/
void assertEqual(const char *message, const byte *expectedValues, size_t expectedSize, const pair<unique_ptr<char[], StringDataDeleter>, 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<byte *>(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<const char *>(simpleString), 4));
assertEqual("Latin-1 to UTF-8", utf8String, 6, convertLatin1ToUtf8(reinterpret_cast<const char *>(latin1String), 5));
assertEqual("UTF-16LE to UTF-8 (simple)", simpleString, 4, convertUtf16LEToUtf8(reinterpret_cast<const char *>(LE_STR_FOR_ENDIANNESS(simpleUtf16)), 8));
assertEqual("UTF-16LE to UTF-8", utf8String, 6, convertUtf16LEToUtf8(reinterpret_cast<const char *>(LE_STR_FOR_ENDIANNESS(utf16)), 10));
assertEqual("UTF-16BE to UTF-8 (simple)", simpleString, 4, convertUtf16BEToUtf8(reinterpret_cast<const char *>(BE_STR_FOR_ENDIANNESS(simpleUtf16)), 8));
assertEqual("UTF-16BE to UTF-8", utf8String, 6, convertUtf16BEToUtf8(reinterpret_cast<const char *>(BE_STR_FOR_ENDIANNESS(utf16)), 10));
// test conversion from UTF-8
assertEqual("UTF-8 to Latin-1 (simple)", simpleString, 4, convertUtf8ToLatin1(reinterpret_cast<const char *>(simpleString), 4));
assertEqual("UTF-8 to Latin-1", latin1String, 5, convertUtf8ToLatin1(reinterpret_cast<const char *>(utf8String), 6));
assertEqual("UTF-8 to UFT-16LE (simple)", reinterpret_cast<const byte *>(LE_STR_FOR_ENDIANNESS(simpleUtf16)), 8, convertUtf8ToUtf16LE(reinterpret_cast<const char *>(simpleString), 4));
assertEqual("UTF-8 to UFT-16LE", reinterpret_cast<const byte *>(LE_STR_FOR_ENDIANNESS(utf16)), 10, convertUtf8ToUtf16LE(reinterpret_cast<const char *>(utf8String), 6));
assertEqual("UTF-8 to UFT-16BE (simple)", reinterpret_cast<const byte *>(BE_STR_FOR_ENDIANNESS(simpleUtf16)), 8, convertUtf8ToUtf16BE(reinterpret_cast<const char *>(simpleString), 4));
assertEqual("UTF-8 to UFT-16BE", reinterpret_cast<const byte *>(BE_STR_FOR_ENDIANNESS(utf16)), 10, convertUtf8ToUtf16BE(reinterpret_cast<const char *>(utf8String), 6));
}
/*!
* \brief Tests miscellaneous string conversions.
2016-01-27 01:01:43 +01:00
*/
void ConversionTests::testStringConversions()
{
// test stringToNumber() / numberToString() with random numbers
uniform_int_distribution<int64> randomDistSigned(numeric_limits<int64>::min());
uniform_int_distribution<uint64> 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<byte>{2, 8, 10, 16}) {
auto resultString = stringToNumber<uint64, string>(numberToString<uint64, string>(unsignedRandom, base), base);
auto resultWideString = stringToNumber<uint64, wstring>(numberToString<uint64, wstring>(unsignedRandom, base), base);
CPPUNIT_ASSERT(resultString == unsignedRandom);
CPPUNIT_ASSERT(resultWideString == unsignedRandom);
}
for(const auto base : initializer_list<byte>{10}) {
auto resultString = stringToNumber<int64, string>(numberToString<int64, string>(signedRandom, base), base);
auto resultWideString = stringToNumber<int64, wstring>(numberToString<int64, wstring>(signedRandom, base), base);
CPPUNIT_ASSERT(resultString == signedRandom);
CPPUNIT_ASSERT(resultWideString == signedRandom);
}
2016-01-25 23:59:52 +01:00
}
2016-01-27 01:01:43 +01:00
// test interpretIntegerAsString()
CPPUNIT_ASSERT(interpretIntegerAsString<uint32>(0x54455354) == "TEST");
// test splitString() / joinStrings()
auto splitJoinTest = joinStrings(splitString<vector<string> >(",a,,ab,ABC,s", ",", EmptyPartsTreat::Keep), " ", false, "(", ")");
CPPUNIT_ASSERT(splitJoinTest == "() (a) () (ab) (ABC) (s)");
splitJoinTest = joinStrings(splitString<vector<string> >(",a,,ab,ABC,s", ",", EmptyPartsTreat::Keep), " ", true, "(", ")");
CPPUNIT_ASSERT(splitJoinTest == "(a) (ab) (ABC) (s)");
splitJoinTest = joinStrings(splitString<vector<string> >(",a,,ab,ABC,s", ",", EmptyPartsTreat::Omit), " ", false, "(", ")");
CPPUNIT_ASSERT(splitJoinTest == "(a) (ab) (ABC) (s)");
splitJoinTest = joinStrings(splitString<vector<string> >(",a,,ab,ABC,s", ",", EmptyPartsTreat::Merge), " ", false, "(", ")");
CPPUNIT_ASSERT(splitJoinTest == "(a,ab) (ABC) (s)");
// test findAndReplace()
string findReplaceTest("findAndReplace()");
findAndReplace<string>(findReplaceTest, "And", "Or");
CPPUNIT_ASSERT(findReplaceTest == "findOrReplace()");
// test startsWith()
CPPUNIT_ASSERT(!startsWith<string>(findReplaceTest, "findAnd"));
CPPUNIT_ASSERT(startsWith<string>(findReplaceTest, "findOr"));
// test encodeBase64() / decodeBase64() with random data
uniform_int_distribution<byte> 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]);
}
2016-01-25 23:59:52 +01:00
}