3 #include "../conversion/conversionexception.h"
4 #include "../conversion/stringbuilder.h"
6 #include "../io/ansiescapecodes.h"
7 #include "../io/binaryreader.h"
8 #include "../io/binarywriter.h"
9 #include "../io/bitreader.h"
10 #include "../io/copy.h"
11 #include "../io/inifile.h"
12 #include "../io/misc.h"
13 #include "../io/nativefilestream.h"
14 #include "../io/path.h"
16 #include <cppunit/TestFixture.h>
17 #include <cppunit/extensions/HelperMacros.h>
24 #ifdef PLATFORM_WINDOWS
29 #include <sys/fcntl.h>
30 #include <sys/types.h>
36 using namespace CPPUNIT_NS;
43 CPPUNIT_TEST(testBinaryReader);
44 CPPUNIT_TEST(testBinaryWriter);
45 CPPUNIT_TEST(testBitReader);
46 CPPUNIT_TEST(testPathUtilities);
47 CPPUNIT_TEST(testIniFile);
48 CPPUNIT_TEST(testAdvancedIniFile);
49 CPPUNIT_TEST(testCopy);
50 CPPUNIT_TEST(testReadFile);
51 CPPUNIT_TEST(testWriteFile);
52 CPPUNIT_TEST(testAnsiEscapeCodes);
53 #ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
54 CPPUNIT_TEST(testNativeFileStream);
56 CPPUNIT_TEST_SUITE_END();
59 void setUp()
override;
60 void tearDown()
override;
62 void testBinaryReader();
63 void testBinaryWriter();
65 void testPathUtilities();
67 void testAdvancedIniFile();
71 void testAnsiEscapeCodes();
72 #ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
73 void testNativeFileStream();
94 testFile.exceptions(ios_base::failbit | ios_base::badbit);
95 testFile.open(
testFilePath(
"some_data"), ios_base::in | ios_base::binary);
97 CPPUNIT_ASSERT_EQUAL(
static_cast<istream::pos_type
>(398), reader.
readStreamsize());
98 CPPUNIT_ASSERT_EQUAL(
static_cast<std::uint16_t
>(0x0102u), reader.
readUInt16LE());
99 CPPUNIT_ASSERT_EQUAL(
static_cast<istream::pos_type
>(396), reader.
readRemainingBytes());
100 CPPUNIT_ASSERT_EQUAL(
static_cast<std::uint16_t
>(0x0102u), reader.
readUInt16BE());
103 CPPUNIT_ASSERT_EQUAL(0x01020304u, reader.
readUInt32LE());
104 CPPUNIT_ASSERT_EQUAL(0x01020304u, reader.
readUInt32BE());
105 CPPUNIT_ASSERT_EQUAL(0x0102030405u, reader.
readUInt40LE());
106 CPPUNIT_ASSERT_EQUAL(0x0102030405u, reader.
readUInt40BE());
107 CPPUNIT_ASSERT_EQUAL(0x01020304050607u, reader.
readUInt56LE());
108 CPPUNIT_ASSERT_EQUAL(0x01020304050607u, reader.
readUInt56BE());
109 CPPUNIT_ASSERT_EQUAL(0x0102030405060708u, reader.
readUInt64LE());
110 CPPUNIT_ASSERT_EQUAL(0x0102030405060708u, reader.
readUInt64BE());
112 CPPUNIT_ASSERT_EQUAL(reader.
readInt16LE(),
static_cast<std::int16_t
>(0x0102));
113 CPPUNIT_ASSERT_EQUAL(reader.
readInt16BE(),
static_cast<std::int16_t
>(0x0102));
114 CPPUNIT_ASSERT_EQUAL(0x010203, reader.
readInt24LE());
115 CPPUNIT_ASSERT_EQUAL(0x010203, reader.
readInt24BE());
116 CPPUNIT_ASSERT_EQUAL(0x01020304, reader.
readInt32LE());
117 CPPUNIT_ASSERT_EQUAL(0x01020304, reader.
readInt32BE());
118 CPPUNIT_ASSERT_EQUAL(0x0102030405, reader.
readInt40LE());
119 CPPUNIT_ASSERT_EQUAL(0x0102030405, reader.
readInt40BE());
120 CPPUNIT_ASSERT_EQUAL(0x01020304050607, reader.
readInt56LE());
121 CPPUNIT_ASSERT_EQUAL(0x01020304050607, reader.
readInt56BE());
122 CPPUNIT_ASSERT_EQUAL(0x0102030405060708, reader.
readInt64LE());
123 CPPUNIT_ASSERT_EQUAL(0x0102030405060708, reader.
readInt64BE());
128 CPPUNIT_ASSERT_EQUAL(
false, reader.
readBool());
129 CPPUNIT_ASSERT_EQUAL(
true, reader.
readBool());
130 CPPUNIT_ASSERT_EQUAL(
"abc"s, reader.
readString(3));
132 CPPUNIT_ASSERT_EQUAL(
"01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901"
133 "23456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123"
134 "45678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345"
135 "678901234567890123456789"s,
138 testFile.seekg(-4, ios_base::cur);
141 CPPUNIT_ASSERT_MESSAGE(
"pos in stream not advanced on conversion error", reader.
readByte() == 0);
160 testFile.exceptions(ios_base::failbit | ios_base::badbit);
161 testFile.open(
testFilePath(
"some_data"), ios_base::in | ios_base::binary);
164 stringstream outputStream(ios_base::in | ios_base::out | ios_base::binary);
165 outputStream.exceptions(ios_base::failbit | ios_base::badbit);
167 outputStream.rdbuf()->pubsetbuf(testData,
sizeof(testData));
185 for (
char c : testData) {
186 CPPUNIT_ASSERT(c ==
static_cast<char>(testFile.get()));
187 if (testFile.tellg() >= 58) {
192 outputStream.seekp(0);
215 writer.
writeLengthPrefixedString(
"012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
216 "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901"
217 "234567890123456789012345678901234567890123456789012345678901234567890123456789");
221 for (
char c : testData) {
222 CPPUNIT_ASSERT(c ==
static_cast<char>(testFile.get()));
240 const std::uint8_t testData[] = { 0x81, 0x90, 0x3C, 0x44, 0x28, 0x00, 0x44, 0x10, 0x20, 0xFF, 0xFA };
241 BitReader reader(
reinterpret_cast<const char *
>(testData),
sizeof(testData));
242 CPPUNIT_ASSERT(reader.
readBit() == 1);
244 CPPUNIT_ASSERT_EQUAL(
static_cast<std::uint8_t
>(3), reader.
showBits<std::uint8_t>(2));
245 CPPUNIT_ASSERT_EQUAL(
static_cast<std::uint8_t
>(3), reader.
readBits<std::uint8_t>(2));
246 CPPUNIT_ASSERT_EQUAL(
static_cast<std::uint32_t
>(0x103C4428 << 1), reader.
readBits<std::uint32_t>(32));
248 CPPUNIT_ASSERT_EQUAL(
static_cast<std::uint8_t
>(0x44), reader.
readBits<std::uint8_t>(8));
251 CPPUNIT_ASSERT_EQUAL(
static_cast<std::uint8_t
>(0), reader.
readBit());
252 CPPUNIT_ASSERT_EQUAL(
static_cast<std::uint8_t
>(0), reader.
readBit());
255 CPPUNIT_ASSERT_EQUAL(
static_cast<std::uint8_t
>(0xA), reader.
readBits<std::uint8_t>(4));
256 CPPUNIT_ASSERT_THROW(reader.
readBit(), std::ios_base::failure);
257 CPPUNIT_ASSERT_THROW(reader.
skipBits(1), std::ios_base::failure);
258 reader.
reset(
reinterpret_cast<const char *
>(testData),
sizeof(testData));
259 CPPUNIT_ASSERT_EQUAL(
static_cast<std::size_t
>(8 *
sizeof(testData)), reader.
bitsAvailable());
267 CPPUNIT_ASSERT_EQUAL(
"libc++utilities.so"s,
fileName(
"C:\\libs\\libc++utilities.so"));
268 CPPUNIT_ASSERT_EQUAL(
"libc++utilities.so"s,
fileName(
"C:\\libs/libc++utilities.so"));
269 CPPUNIT_ASSERT_EQUAL(
"libc++utilities.so"s,
fileName(
"/usr/lib/libc++utilities.so"));
270 CPPUNIT_ASSERT_EQUAL(
"libc++utilities.so"s,
fileName(
"libc++utilities.so"));
271 CPPUNIT_ASSERT_EQUAL(
"/usr/lib/"s,
directory(
"/usr/lib/libc++utilities.so"));
272 CPPUNIT_ASSERT_EQUAL(
string(),
directory(
"libc++utilities.so"));
273 CPPUNIT_ASSERT_EQUAL(
"C:\\libs\\"s,
directory(
"C:\\libs\\libc++utilities.so"));
274 CPPUNIT_ASSERT_EQUAL(
"C:\\libs/"s,
directory(
"C:\\libs/libc++utilities.so"));
275 string invalidPath(
"lib/c++uti*lities.so?");
277 CPPUNIT_ASSERT(invalidPath ==
"libc++utilities.so");
287 inputFile.exceptions(ios_base::failbit | ios_base::badbit);
291 ini.
parse(inputFile);
292 const auto globalScope = ini.
data().at(0);
293 const auto scope1 = ini.
data().at(1);
294 const auto scope2 = ini.
data().at(2);
295 CPPUNIT_ASSERT(globalScope.first.empty());
296 CPPUNIT_ASSERT(globalScope.second.find(
"key0") != globalScope.second.cend());
297 CPPUNIT_ASSERT(globalScope.second.find(
"key0")->second ==
"value 0");
298 CPPUNIT_ASSERT(globalScope.second.find(
"key1") == globalScope.second.cend());
299 CPPUNIT_ASSERT(scope1.first ==
"scope 1");
300 CPPUNIT_ASSERT(scope1.second.find(
"key1") != scope1.second.cend());
301 CPPUNIT_ASSERT(scope1.second.find(
"key1")->second ==
"value 1");
302 CPPUNIT_ASSERT(scope1.second.find(
"key2") != scope1.second.cend());
303 CPPUNIT_ASSERT(scope1.second.find(
"key2")->second ==
"value=2");
304 CPPUNIT_ASSERT(scope2.first ==
"scope 2");
305 CPPUNIT_ASSERT(scope2.second.find(
"key5") == scope2.second.cend());
309 outputFile.exceptions(ios_base::failbit | ios_base::badbit);
310 outputFile.open(
workingCopyPath(
"output.ini", WorkingCopyMode::NoCopy), ios_base::out | ios_base::trunc);
311 ini.
make(outputFile);
315 outputFile.open(
workingCopyPath(
"output.ini", WorkingCopyMode::NoCopy), ios_base::in);
317 ini2.
parse(outputFile);
318 CPPUNIT_ASSERT(ini.
data() == ini2.
data());
328 inputFile.exceptions(ios_base::failbit | ios_base::badbit);
329 inputFile.open(
testFilePath(
"pacman.conf"), ios_base::in);
333 ini.
parse(inputFile);
336 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"5 scopes (taking implicit empty section at the end into account)", 5_st, ini.
sections.size());
340 "comment block before section",
"# Based on.*\n.*# GENERAL OPTIONS\n#\n"s, std::regex::extended, options->precedingCommentBlock);
341 CPPUNIT_ASSERT_EQUAL(7_st, options->fields.size());
342 CPPUNIT_ASSERT_EQUAL(
"HoldPkg"s, options->fields[0].key);
343 CPPUNIT_ASSERT_EQUAL(
"pacman glibc"s, options->fields[0].value);
344 CPPUNIT_ASSERT_MESSAGE(
"value present", options->fields[0].flags & IniFileFieldFlags::HasValue);
346 "# The following paths are.*\n.*#HookDir = /etc/pacman\\.d/hooks/\n"s, std::regex::extended, options->fields[0].precedingCommentBlock);
347 CPPUNIT_ASSERT_EQUAL(
""s, options->fields[0].followingInlineComment);
348 CPPUNIT_ASSERT_EQUAL(
"Foo"s, options->fields[1].key);
349 CPPUNIT_ASSERT_EQUAL(
"bar"s, options->fields[1].value);
350 CPPUNIT_ASSERT_MESSAGE(
"value present", options->fields[1].flags & IniFileFieldFlags::HasValue);
351 TESTUTILS_ASSERT_LIKE_FLAGS(
"comment block between fields",
"#XferCommand.*\n.*#CleanMethod = KeepInstalled\n"s, std::regex::extended,
352 options->fields[1].precedingCommentBlock);
353 CPPUNIT_ASSERT_EQUAL(
"# inline comment"s, options->fields[1].followingInlineComment);
354 CPPUNIT_ASSERT_EQUAL(
"CheckSpace"s, options->fields[3].key);
355 CPPUNIT_ASSERT_EQUAL(
""s, options->fields[3].value);
356 CPPUNIT_ASSERT_MESSAGE(
"no value present", !(options->fields[3].flags & IniFileFieldFlags::HasValue));
357 TESTUTILS_ASSERT_LIKE_FLAGS(
"empty lines in comments preserved",
"\n# Pacman.*\n.*\n\n#NoUpgrade =\n.*#TotalDownload\n"s, std::regex::extended,
358 options->fields[3].precedingCommentBlock);
359 CPPUNIT_ASSERT_EQUAL(
""s, options->fields[3].followingInlineComment);
360 auto extraScope = ini.
findSection(options,
"extra");
361 CPPUNIT_ASSERT(extraScope != ini.
sectionEnd());
362 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"comment block which is only an empty line",
"\n"s, extraScope->precedingCommentBlock);
363 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"inline comment after scope",
"# an inline comment after a scope name"s, extraScope->followingInlineComment);
364 CPPUNIT_ASSERT_EQUAL(1_st, extraScope->fields.size());
365 CPPUNIT_ASSERT(ini.
sections.back().flags & IniFileSectionFlags::Implicit);
366 TESTUTILS_ASSERT_LIKE_FLAGS(
"comment block after last field present in implicitly added last scope",
"\n# If you.*\n.*custompkgs\n"s,
367 std::regex::extended, ini.
sections.back().precedingCommentBlock);
370 const auto *
const constIniFile = &ini;
371 auto includeField = constIniFile->
findField(
"extra",
"Include");
372 CPPUNIT_ASSERT(includeField.has_value());
373 CPPUNIT_ASSERT_EQUAL(
"Include"s, includeField.value()->key);
374 CPPUNIT_ASSERT_EQUAL(
"/etc/pacman.d/mirrorlist"s, includeField.value()->value);
375 CPPUNIT_ASSERT_MESSAGE(
"field not present", !constIniFile->findField(
"extra",
"Includ").has_value());
376 CPPUNIT_ASSERT_MESSAGE(
"scope not present", !constIniFile->findField(
"extr",
"Includ").has_value());
380 std::stringstream newFile;
382 std::string originalContents;
384 inputFile.seekg(std::ios_base::beg);
385 originalContents.assign((istreambuf_iterator<char>(inputFile)), istreambuf_iterator<char>());
386 CPPUNIT_ASSERT_EQUAL(originalContents, newFile.str());
396 testFile.exceptions(ios_base::failbit | ios_base::badbit);
397 testFile.open(
testFilePath(
"some_data"), ios_base::in | ios_base::binary);
398 stringstream outputStream(ios_base::in | ios_base::out | ios_base::binary);
399 outputStream.exceptions(ios_base::failbit | ios_base::badbit);
403 copyHelper.
copy(testFile, outputStream, 50);
407 for (
auto i = 0;
i < 50; ++
i) {
408 CPPUNIT_ASSERT(testFile.get() == outputStream.get());
419 CPPUNIT_ASSERT_EQUAL(
"# file for testing INI parser\n"
423 "key1=value 1 # comment\n"
434 CPPUNIT_ASSERT_THROW(
readFile(iniFilePath, 10), std::ios_base::failure);
437 #if !defined(PLATFORM_WINDOWS) || defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER)
438 CPPUNIT_ASSERT_EQUAL(
"file with non-ASCII character 'ä' in its name\n"s,
readFile(
testFilePath(
"täst.txt")));
447 const string path(
workingCopyPath(
"test.ini", WorkingCopyMode::NoCopy));
449 CPPUNIT_ASSERT_EQUAL(
"some contents"s,
readFile(path));
459 ss1 << EscapeCodes::Phrases::Error <<
"some error" << EscapeCodes::Phrases::End;
460 ss1 << EscapeCodes::Phrases::Warning <<
"some warning" << EscapeCodes::Phrases::End;
461 ss1 << EscapeCodes::Phrases::Info <<
"some info" << EscapeCodes::Phrases::End;
462 ss1 << EscapeCodes::Phrases::ErrorMessage <<
"Arch-style error" << EscapeCodes::Phrases::End;
463 ss1 << EscapeCodes::Phrases::WarningMessage <<
"Arch-style warning" << EscapeCodes::Phrases::End;
464 ss1 << EscapeCodes::Phrases::PlainMessage <<
"Arch-style message" << EscapeCodes::Phrases::End;
465 ss1 << EscapeCodes::Phrases::SuccessMessage <<
"Arch-style success" << EscapeCodes::Phrases::End;
466 ss1 << EscapeCodes::Phrases::SubMessage <<
"Arch-style sub-message" << EscapeCodes::Phrases::End;
467 ss1 <<
EscapeCodes::color(EscapeCodes::Color::Blue, EscapeCodes::Color::Red, EscapeCodes::TextAttribute::Blink)
468 <<
"blue, blinking text on red background" << EscapeCodes::TextAttribute::Reset <<
'\n';
469 cout <<
"\noutput for formatting with ANSI escape codes:\n" << ss1.str() <<
"---------------------------------------------\n";
470 fstream(
"/tmp/test.txt", ios_base::out | ios_base::trunc) << ss1.str();
471 CPPUNIT_ASSERT_EQUAL(
"\e[1;31mError: \e[0m\e[1msome error\e[0m\n"
472 "\e[1;33mWarning: \e[0m\e[1msome warning\e[0m\n"
473 "\e[1;34mInfo: \e[0m\e[1msome info\e[0m\n"
474 "\e[1;31m==> ERROR: \e[0m\e[1mArch-style error\e[0m\n"
475 "\e[1;33m==> WARNING: \e[0m\e[1mArch-style warning\e[0m\n"
476 " \e[0m\e[1mArch-style message\e[0m\n"
477 "\e[1;32m==> \e[0m\e[1mArch-style success\e[0m\n"
478 "\e[1;32m -> \e[0m\e[1mArch-style sub-message\e[0m\n"
479 "\e[5;34;41mblue, blinking text on red background\e[0m\n"s,
484 ss2 << EscapeCodes::Phrases::Info <<
"some info" << EscapeCodes::Phrases::End;
485 CPPUNIT_ASSERT_EQUAL(
"Info: some info\n"s, ss2.str());
488 #ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
492 void IoTests::testNativeFileStream()
497 fileStream.exceptions(ios_base::badbit | ios_base::failbit);
498 CPPUNIT_ASSERT(!fileStream.is_open());
499 fileStream.open(txtFilePath, ios_base::in);
500 CPPUNIT_ASSERT(fileStream.is_open());
501 #if defined(PLATFORM_WINDOWS) && defined(CPP_UTILITIES_USE_BOOST_IOSTREAMS)
502 CPPUNIT_ASSERT(fileStream.fileHandle() !=
nullptr);
504 CPPUNIT_ASSERT(fileStream.fileDescriptor() != -1);
506 CPPUNIT_ASSERT_EQUAL(
static_cast<char>(fileStream.get()),
'f');
507 fileStream.seekg(0, ios_base::end);
508 CPPUNIT_ASSERT_EQUAL(fileStream.tellg(),
static_cast<NativeFileStream::pos_type
>(47));
510 CPPUNIT_ASSERT(!fileStream.is_open());
512 fileStream.open(
"non existing file", ios_base::in | ios_base::out | ios_base::binary);
513 CPPUNIT_FAIL(
"expected exception");
514 }
catch (
const std::ios_base::failure &failure) {
515 #ifdef PLATFORM_WINDOWS
516 #ifdef CPP_UTILITIES_USE_BOOST_IOSTREAMS
528 #ifndef PLATFORM_WINDOWS
529 auto readWriteFileDescriptor = open(txtFilePath.data(), O_RDWR);
530 CPPUNIT_ASSERT(readWriteFileDescriptor);
531 fileStream.open(readWriteFileDescriptor, ios_base::in | ios_base::out | ios_base::binary);
532 CPPUNIT_ASSERT(fileStream.is_open());
533 CPPUNIT_ASSERT_EQUAL(
static_cast<char>(fileStream.get()),
'f');
534 fileStream.seekg(0, ios_base::end);
535 CPPUNIT_ASSERT_EQUAL(fileStream.tellg(),
static_cast<NativeFileStream::pos_type
>(47));
538 CPPUNIT_ASSERT(!fileStream.is_open());
541 fileStream.open(-1, ios_base::in | ios_base::out | ios_base::binary);
543 CPPUNIT_FAIL(
"expected exception");
544 }
catch (
const std::ios_base::failure &failure) {
545 #ifndef PLATFORM_WINDOWS
547 "expected error message",
"(basic_ios::clear|failed reading: Bad file descriptor): iostream error"s,
string(failure.what()));
556 fileStream2.exceptions(ios_base::failbit | ios_base::badbit);
557 fileStream2.open(txtFilePath, ios_base::in | ios_base::out | ios_base::app);
558 CPPUNIT_ASSERT(fileStream2.is_open());
559 fileStream2 <<
"foo";
562 CPPUNIT_ASSERT(!fileStream2.is_open());
563 CPPUNIT_ASSERT_EQUAL(
"file with non-ASCII character 'ä' in its name\nfoo"s,
readFile(txtFilePath, 50));
566 fileStream2.open(txtFilePath, ios_base::out | ios_base::trunc);
567 CPPUNIT_ASSERT(fileStream2.is_open());
568 fileStream2 <<
"bar";
570 CPPUNIT_ASSERT(!fileStream2.is_open());
571 CPPUNIT_ASSERT_EQUAL(
"bar"s,
readFile(txtFilePath, 4));
574 #ifdef PLATFORM_WINDOWS
575 const auto wideTxtFilePath = NativeFileStream::makeWidePath(txtFilePath);
576 const auto appendFileHandle = _wfopen(wideTxtFilePath.get(), L
"a+");
578 const auto appendFileHandle = fopen(txtFilePath.data(),
"a");
580 CPPUNIT_ASSERT(appendFileHandle);
581 fileStream2.open(fileno(appendFileHandle), ios_base::out | ios_base::app);
582 CPPUNIT_ASSERT(fileStream2.is_open());
583 fileStream2 <<
"foo";
585 CPPUNIT_ASSERT(!fileStream2.is_open());
586 CPPUNIT_ASSERT_EQUAL(
"barfoo"s,
readFile(txtFilePath, 7));