C++ Utilities  4.7.0
Common C++ classes and routines used by my applications such as argument parser, IO and conversion utilities
conversiontests.cpp
Go to the documentation of this file.
1 #include "../conversion/binaryconversion.h"
2 #include "../conversion/stringbuilder.h"
3 #include "../conversion/stringconversion.h"
4 #include "../tests/testutils.h"
5 
6 #include <cppunit/TestFixture.h>
7 #include <cppunit/extensions/HelperMacros.h>
8 
9 #include <functional>
10 #include <initializer_list>
11 #include <random>
12 #include <sstream>
13 
14 using namespace std;
15 using namespace ConversionUtilities;
16 using namespace TestUtilities;
17 
18 using namespace CPPUNIT_NS;
19 
23 class ConversionTests : public TestFixture {
24  CPPUNIT_TEST_SUITE(ConversionTests);
25  CPPUNIT_TEST(testEndianness);
26  CPPUNIT_TEST(testBinaryConversions);
27  CPPUNIT_TEST(testSwapOrderFunctions);
28  CPPUNIT_TEST(testStringEncodingConversions);
29  CPPUNIT_TEST(testStringConversions);
30  CPPUNIT_TEST(testStringBuilder);
31  CPPUNIT_TEST_SUITE_END();
32 
33 public:
35 
36  void setUp()
37  {
38  }
39  void tearDown()
40  {
41  }
42 
43  void testEndianness();
44  void testBinaryConversions();
45  void testSwapOrderFunctions();
46  void testStringEncodingConversions();
47  void testStringConversions();
48  void testStringBuilder();
49 
50 private:
51  template <typename intType>
52  void testConversion(const char *message, function<void(intType, char *)> vice, function<intType(const char *)> verca, intType min, intType max);
53 
54  char m_buff[8];
55  random_device m_randomDevice;
56  mt19937 m_randomEngine;
57 };
58 
60 
62  : m_randomDevice()
63  , m_randomEngine(m_randomDevice())
64 {
65 }
66 
71 {
72  union {
73  uint32_t integer;
74  char characters[4];
75  } test = { 0x01020304 };
76 #if defined(CONVERSION_UTILITIES_BYTE_ORDER_BIG_ENDIAN)
77  // test whether macro definitions are consistent
78  CPPUNIT_ASSERT(CONVERSION_UTILITIES_IS_BYTE_ORDER_BIG_ENDIAN == true);
79  CPPUNIT_ASSERT(CONVERSION_UTILITIES_IS_BYTE_ORDER_LITTLE_ENDIAN == false);
80  // test whether byte order assumption is correct
81  CPPUNIT_ASSERT_MESSAGE("Byte order assumption (big-endian) is wrong", test.characters[0] == 0x01);
82 #elif defined(CONVERSION_UTILITIES_BYTE_ORDER_LITTLE_ENDIAN)
83  // test whether macro definitions are consistent
84  CPPUNIT_ASSERT(CONVERSION_UTILITIES_IS_BYTE_ORDER_BIG_ENDIAN == false);
85  CPPUNIT_ASSERT(CONVERSION_UTILITIES_IS_BYTE_ORDER_LITTLE_ENDIAN == true);
86  // test whether byte order assumption is correct
87  CPPUNIT_ASSERT_MESSAGE("Byte order assumption (little-endian) is wrong", test.characters[0] == 0x04);
88 #else
89  CPPUNIT_FAIL("There is not valid byte order assumption");
90 #endif
91 }
92 
93 template <typename intType>
94 void ConversionTests::testConversion(
95  const char *message, function<void(intType, char *)> vice, function<intType(const char *)> versa, intType min, intType max)
96 {
97  const intType random = uniform_int_distribution<intType>(min, max)(m_randomEngine);
98  stringstream msg;
99  msg << message << '(' << hex << '0' << 'x' << random << ')';
100  vice(random, m_buff);
101  CPPUNIT_ASSERT_MESSAGE(msg.str(), versa(m_buff) == random);
102 }
103 
104 #define TEST_TYPE(endianness, function) decltype(endianness::function(m_buff))
105 
106 #define TEST_CONVERSION(function, endianness) \
107  testConversion<TEST_TYPE(endianness, function)>("testing " #function, \
108  static_cast<void (*)(TEST_TYPE(endianness, function), char *)>(&endianness::getBytes), endianness::function, \
109  numeric_limits<TEST_TYPE(endianness, function)>::min(), numeric_limits<TEST_TYPE(endianness, function)>::max())
110 
111 #define TEST_BE_CONVERSION(function) TEST_CONVERSION(function, BE)
112 
113 #define TEST_LE_CONVERSION(function) TEST_CONVERSION(function, LE)
114 
115 #define TEST_CUSTOM_CONVERSION(vice, versa, endianness, min, max) \
116  testConversion<TEST_TYPE(endianness, versa)>( \
117  "testing " #versa, static_cast<void (*)(TEST_TYPE(endianness, versa), char *)>(&endianness::vice), endianness::versa, min, max)
118 
126 {
127  // test to...() / getBytes() with random numbers
128  for (byte b = 1; b < 100; ++b) {
129  TEST_BE_CONVERSION(toUInt16);
130  TEST_BE_CONVERSION(toUInt32);
131  TEST_BE_CONVERSION(toUInt64);
132  TEST_LE_CONVERSION(toUInt16);
133  TEST_LE_CONVERSION(toUInt32);
134  TEST_LE_CONVERSION(toUInt64);
135  TEST_BE_CONVERSION(toInt16);
136  TEST_BE_CONVERSION(toInt32);
137  TEST_BE_CONVERSION(toInt64);
138  TEST_LE_CONVERSION(toInt16);
139  TEST_LE_CONVERSION(toInt32);
140  TEST_LE_CONVERSION(toInt64);
141  TEST_CUSTOM_CONVERSION(getBytes24, toUInt24, BE, 0, 0xFFFFFF);
142  TEST_CUSTOM_CONVERSION(getBytes24, toUInt24, LE, 0, 0xFFFFFF);
143  }
144 }
145 
150 {
151  CPPUNIT_ASSERT(swapOrder(static_cast<uint16>(0x7825)) == 0x2578);
152  CPPUNIT_ASSERT(swapOrder(static_cast<uint32>(0x12345678)) == 0x78563412);
153  CPPUNIT_ASSERT(swapOrder(static_cast<uint64>(0x1122334455667788)) == 0x8877665544332211);
154 }
155 
159 void assertEqual(const char *message, const byte *expectedValues, size_t expectedSize, const StringData &actualValues)
160 {
161  // check whether number of elements matches
162  CPPUNIT_ASSERT_EQUAL_MESSAGE(message, expectedSize, actualValues.second);
163  // check whether contents match
164  auto *end = expectedValues + expectedSize;
165  auto *i = reinterpret_cast<byte *>(actualValues.first.get());
166  for (; expectedValues != end; ++expectedValues, ++i) {
167  CPPUNIT_ASSERT_EQUAL_MESSAGE(message, asHexNumber(*expectedValues), asHexNumber(*i));
168  }
169 }
170 
171 #if CONVERSION_UTILITIES_IS_BYTE_ORDER_LITTLE_ENDIAN == true
172 #define LE_STR_FOR_ENDIANNESS(name) name##LE##String
173 #define BE_STR_FOR_ENDIANNESS(name) name##BE##String
174 #elif CONVERSION_UTILITIES_IS_BYTE_ORDER_BIG_ENDIAN == true
175 #define LE_STR_FOR_ENDIANNESS(name) name##BE##String
176 #define BE_STR_FOR_ENDIANNESS(name) name##LE##String
177 #endif
178 
193 {
194  // define test string "ABCD" for the different encodings
195  const byte simpleString[] = { 'A', 'B', 'C', 'D' };
196  const uint16 simpleUtf16LEString[] = { 0x0041, 0x0042, 0x0043, 0x0044 };
197  const uint16 simpleUtf16BEString[] = { 0x4100, 0x4200, 0x4300, 0x4400 };
198  // define test string "ABĂ–CD" for the different encodings
199  const byte latin1String[] = { 'A', 'B', 0xD6, 'C', 'D' };
200  const byte utf8String[] = { 'A', 'B', 0xC3, 0x96, 'C', 'D' };
201  const uint16 utf16LEString[] = { 0x0041, 0x0042, 0x00D6, 0x0043, 0x0044 };
202  const uint16 utf16BEString[] = { 0x4100, 0x4200, 0xD600, 0x4300, 0x4400 };
203  // test conversion to UTF-8
204  assertEqual("Latin-1 to UTF-8 (simple)", simpleString, 4, convertLatin1ToUtf8(reinterpret_cast<const char *>(simpleString), 4));
205  assertEqual("Latin-1 to UTF-8", utf8String, 6, convertLatin1ToUtf8(reinterpret_cast<const char *>(latin1String), 5));
206  assertEqual(
207  "UTF-16LE to UTF-8 (simple)", simpleString, 4, convertUtf16LEToUtf8(reinterpret_cast<const char *>(LE_STR_FOR_ENDIANNESS(simpleUtf16)), 8));
208  assertEqual("UTF-16LE to UTF-8", utf8String, 6, convertUtf16LEToUtf8(reinterpret_cast<const char *>(LE_STR_FOR_ENDIANNESS(utf16)), 10));
209  assertEqual(
210  "UTF-16BE to UTF-8 (simple)", simpleString, 4, convertUtf16BEToUtf8(reinterpret_cast<const char *>(BE_STR_FOR_ENDIANNESS(simpleUtf16)), 8));
211  assertEqual("UTF-16BE to UTF-8", utf8String, 6, convertUtf16BEToUtf8(reinterpret_cast<const char *>(BE_STR_FOR_ENDIANNESS(utf16)), 10));
212  // test conversion from UTF-8
213  assertEqual("UTF-8 to Latin-1 (simple)", simpleString, 4, convertUtf8ToLatin1(reinterpret_cast<const char *>(simpleString), 4));
214  assertEqual("UTF-8 to Latin-1", latin1String, 5, convertUtf8ToLatin1(reinterpret_cast<const char *>(utf8String), 6));
215  assertEqual("UTF-8 to UFT-16LE (simple)", reinterpret_cast<const byte *>(LE_STR_FOR_ENDIANNESS(simpleUtf16)), 8,
216  convertUtf8ToUtf16LE(reinterpret_cast<const char *>(simpleString), 4));
217  assertEqual("UTF-8 to UFT-16LE", reinterpret_cast<const byte *>(LE_STR_FOR_ENDIANNESS(utf16)), 10,
218  convertUtf8ToUtf16LE(reinterpret_cast<const char *>(utf8String), 6));
219  assertEqual("UTF-8 to UFT-16BE (simple)", reinterpret_cast<const byte *>(BE_STR_FOR_ENDIANNESS(simpleUtf16)), 8,
220  convertUtf8ToUtf16BE(reinterpret_cast<const char *>(simpleString), 4));
221  assertEqual("UTF-8 to UFT-16BE", reinterpret_cast<const byte *>(BE_STR_FOR_ENDIANNESS(utf16)), 10,
222  convertUtf8ToUtf16BE(reinterpret_cast<const char *>(utf8String), 6));
223 }
224 
229 {
230  // stringToNumber() / numberToString() with zero and random numbers
231  CPPUNIT_ASSERT_EQUAL(string("0"), numberToString<unsigned int>(0));
232  CPPUNIT_ASSERT_EQUAL(string("0"), numberToString<signed int>(0));
233  uniform_int_distribution<int64> randomDistSigned(numeric_limits<int64>::min());
234  uniform_int_distribution<uint64> randomDistUnsigned(0);
235  for (byte b = 1; b < 100; ++b) {
236  auto signedRandom = randomDistSigned(m_randomEngine);
237  auto unsignedRandom = randomDistUnsigned(m_randomEngine);
238  for (const auto base : initializer_list<byte>{ 2, 8, 10, 16 }) {
239  auto resultString = stringToNumber<uint64, string>(numberToString<uint64, string>(unsignedRandom, base), base);
240  auto resultWideString = stringToNumber<uint64, wstring>(numberToString<uint64, wstring>(unsignedRandom, base), base);
241  CPPUNIT_ASSERT_EQUAL(unsignedRandom, resultString);
242  CPPUNIT_ASSERT_EQUAL(unsignedRandom, resultWideString);
243  }
244  for (const auto base : initializer_list<byte>{ 10 }) {
245  auto resultString = stringToNumber<int64, string>(numberToString<int64, string>(signedRandom, base), base);
246  auto resultWideString = stringToNumber<int64, wstring>(numberToString<int64, wstring>(signedRandom, base), base);
247  CPPUNIT_ASSERT_EQUAL(signedRandom, resultString);
248  CPPUNIT_ASSERT_EQUAL(signedRandom, resultWideString);
249  }
250  }
251 
252  // stringToNumber() with leading zeroes and different types
253  int32 res = stringToNumber<int32, string>("01");
254  CPPUNIT_ASSERT_EQUAL(1, res);
255  res = stringToNumber<int32, wstring>(L"01");
256  CPPUNIT_ASSERT_EQUAL(1, res);
257  res = stringToNumber<int32, u16string>(u"01");
258  CPPUNIT_ASSERT_EQUAL(1, res);
259 
260  // interpretIntegerAsString()
261  CPPUNIT_ASSERT(interpretIntegerAsString<uint32>(0x54455354) == "TEST");
262 
263  // splitString() / joinStrings()
264  string splitJoinTest = joinStrings(splitString<vector<string>>(",a,,ab,ABC,s"s, ","s, EmptyPartsTreat::Keep), " "s, false, "("s, ")"s);
265  CPPUNIT_ASSERT_EQUAL("() (a) () (ab) (ABC) (s)"s, splitJoinTest);
266  splitJoinTest = joinStrings(splitString<vector<string>>(",a,,ab,ABC,s"s, ","s, EmptyPartsTreat::Keep), " "s, true, "("s, ")"s);
267  CPPUNIT_ASSERT_EQUAL("(a) (ab) (ABC) (s)"s, splitJoinTest);
268  splitJoinTest = joinStrings(splitString<vector<string>>(",a,,ab,ABC,s"s, ","s, EmptyPartsTreat::Omit), " "s, false, "("s, ")"s);
269  CPPUNIT_ASSERT_EQUAL("(a) (ab) (ABC) (s)"s, splitJoinTest);
270  splitJoinTest = joinStrings(splitString<vector<string>>(",a,,ab,ABC,s"s, ","s, EmptyPartsTreat::Merge), " "s, false, "("s, ")"s);
271  CPPUNIT_ASSERT_EQUAL("(a,ab) (ABC) (s)"s, splitJoinTest);
272 
273  // findAndReplace()
274  string findReplaceTest("findAndReplace()");
275  findAndReplace<string>(findReplaceTest, "And", "Or");
276  CPPUNIT_ASSERT_EQUAL("findOrReplace()"s, findReplaceTest);
277 
278  // startsWith()
279  CPPUNIT_ASSERT(!startsWith<string>(findReplaceTest, "findAnd"));
280  CPPUNIT_ASSERT(startsWith<string>(findReplaceTest, "findOr"));
281 
282  // containsSubstrings()
283  CPPUNIT_ASSERT(containsSubstrings<string>("this string contains foo and bar", { "foo", "bar" }));
284  CPPUNIT_ASSERT(!containsSubstrings<string>("this string contains foo and bar", { "bar", "foo" }));
285 
286  // encodeBase64() / decodeBase64() with random data
287  uniform_int_distribution<byte> randomDistChar;
288  byte originalBase64Data[4047];
289  for (byte &c : originalBase64Data) {
290  c = randomDistChar(m_randomEngine);
291  }
292  const auto encodedBase64Data = encodeBase64(originalBase64Data, sizeof(originalBase64Data));
293  auto decodedBase64Data = decodeBase64(encodedBase64Data.data(), encodedBase64Data.size());
294  CPPUNIT_ASSERT(decodedBase64Data.second == sizeof(originalBase64Data));
295  for (unsigned int i = 0; i < sizeof(originalBase64Data); ++i) {
296  CPPUNIT_ASSERT(decodedBase64Data.first[i] == originalBase64Data[i]);
297  }
298 }
299 
300 string functionTakingString(const string &str)
301 {
302  return str;
303 }
304 
306 {
307  // conversion of string-tuple to string (the actual string builder)
308  const tuple<const char *, string, int, const char *> tuple("string1", "string2", 1234, "string3");
309  CPPUNIT_ASSERT_EQUAL(string("string1string21234string3"), tupleToString(tuple));
310  CPPUNIT_ASSERT_EQUAL(string("foobarfoo2bar2"), tupleToString(string("foo") % "bar" % string("foo2") % "bar2"));
311  CPPUNIT_ASSERT_EQUAL(string("v2.3.0"), argsToString("v2.", 3, '.', 0));
312 
313  // construction of string-tuple and final conversion to string works
314  CPPUNIT_ASSERT_EQUAL_MESSAGE(
315  "result can be passed to any function taking a std::string"s, "123456789"s, functionTakingString("12" % string("34") % '5' % 67 + "89"));
316  constexpr double velocityExample = 27.0;
317  CPPUNIT_ASSERT_EQUAL_MESSAGE("real-word example"s, "velocity: 27 km/h (7.5 m/s)"s,
318  functionTakingString("velocity: " % numberToString(velocityExample) % " km/h (" % numberToString(velocityExample / 3.6) + " m/s)"));
319  CPPUNIT_ASSERT_EQUAL_MESSAGE(
320  "regular + operator still works (no problems with ambiguity)"s, "regular + still works"s, "regular"s + " + still works");
321 }
CPP_UTILITIES_EXPORT StringData convertUtf8ToUtf16LE(const char *inputBuffer, std::size_t inputBufferSize)
Converts the specified UTF-8 string to UTF-16 (little-endian).
CPP_UTILITIES_EXPORT std::string encodeBase64(const byte *data, uint32 dataSize)
Encodes the specified data to Base64.
void testSwapOrderFunctions()
Tests swap order functions.
CPP_UTILITIES_EXPORT StringData convertUtf16BEToUtf8(const char *inputBuffer, std::size_t inputBufferSize)
Converts the specified UTF-16 (big-endian) string to UTF-8.
void testBinaryConversions()
Tests most important binary conversions.
constexpr StringType argsToString(Args &&... args)
STL namespace.
#define TEST_LE_CONVERSION(function)
CPPUNIT_TEST_SUITE_REGISTRATION(ConversionTests)
void testStringConversions()
Tests miscellaneous string conversions.
StringType numberToString(IntegralType number, typename StringType::value_type base=10)
Converts the given number to its equivalent string representation using the specified base...
void testEndianness()
Tests whether macros for endianness are correct.
#define TEST_CUSTOM_CONVERSION(vice, versa, endianness, min, max)
CPP_UTILITIES_EXPORT int random(int lowerbounds, int upperbounds)
Returns a pseudo random number between lowerbounds and upperbounds.
Definition: math.cpp:15
#define BE_STR_FOR_ENDIANNESS(name)
Selects right string for big-endian checks.
CPP_UTILITIES_EXPORT StringData convertUtf8ToUtf16BE(const char *inputBuffer, std::size_t inputBufferSize)
Converts the specified UTF-8 string to UTF-16 (big-endian).
string functionTakingString(const string &str)
CPP_UTILITIES_EXPORT constexpr uint16 swapOrder(uint16 value)
Swaps the byte order of the specified 16-bit unsigned integer.
Contains classes and functions utilizing creating of test applications.
Definition: testutils.h:10
#define TEST_BE_CONVERSION(function)
CPP_UTILITIES_EXPORT StringData convertUtf8ToLatin1(const char *inputBuffer, std::size_t inputBufferSize)
Converts the specified UTF-8 string to Latin-1.
Contains several functions providing conversions between different data types.
std::pair< std::unique_ptr< char[], StringDataDeleter >, std::size_t > StringData
Type used to return string encoding conversion result.
CPP_UTILITIES_EXPORT StringData convertUtf16LEToUtf8(const char *inputBuffer, std::size_t inputBufferSize)
Converts the specified UTF-16 (little-endian) string to UTF-8.
AsHexNumber< T > asHexNumber(const T &value)
Wraps a value to be printed using the hex system in the error case when asserted with cppunit (or sim...
Definition: testutils.h:170
void testStringEncodingConversions()
Tests string encoding conversions.
std::int32_t int32
signed 32-bit integer
Definition: types.h:24
void assertEqual(const char *message, const byte *expectedValues, size_t expectedSize, const StringData &actualValues)
Internally used for string encoding tests to check results.
Container::value_type joinStrings(const Container &strings, const typename Container::value_type &delimiter=typename Container::value_type(), bool omitEmpty=false, const typename Container::value_type &leftClosure=typename Container::value_type(), const typename Container::value_type &rightClosure=typename Container::value_type())
Joins the given strings using the specified delimiter.
std::uint8_t byte
unsigned byte
Definition: types.h:14
CPP_UTILITIES_EXPORT StringData convertLatin1ToUtf8(const char *inputBuffer, std::size_t inputBufferSize)
Converts the specified Latin-1 string to UTF-8.
#define LE_STR_FOR_ENDIANNESS(name)
Selects right string for little-endian checks.
StringType tupleToString(const std::tuple< Args... > &tuple)
Concatenates all strings hold by the specified tuple.
Container splitString(const typename Container::value_type &string, const typename Container::value_type &delimiter, EmptyPartsTreat emptyPartsRole=EmptyPartsTreat::Keep, int maxParts=-1)
Splits the given string at the specified delimiter.
CPP_UTILITIES_EXPORT std::pair< std::unique_ptr< byte[]>, uint32 > decodeBase64(const char *encodedStr, const uint32 strSize)
Decodes the specified Base64 encoded string.
The ConversionTests class tests classes and methods of the ConversionUtilities namespace.
std::uint16_t uint16
unsigned 16-bit integer
Definition: types.h:39