C++ Utilities 5.24.8
Useful C++ classes and routines such as argument parser, IO and conversion utilities
Loading...
Searching...
No Matches
iotests.cpp
Go to the documentation of this file.
1#include "./testutils.h"
2
4
8std::ostream &operator<<(std::ostream &out, const std::wstring &s)
9{
10 const auto utf8 = CppUtilities::
11#ifdef CONVERSION_UTILITIES_IS_BYTE_ORDER_LITTLE_ENDIAN
13#else
15#endif
16 (reinterpret_cast<const char *>(s.data()), s.size() * (sizeof(std::wstring::value_type) / sizeof(char)));
17 out.write(utf8.first.get(), static_cast<std::streamsize>(utf8.second));
18 return out;
19}
20
23
25#include "../io/binaryreader.h"
26#include "../io/binarywriter.h"
27#include "../io/bitreader.h"
28#include "../io/buffersearch.h"
29#include "../io/copy.h"
30#include "../io/inifile.h"
31#include "../io/misc.h"
33#include "../io/path.h"
34
35#include <cppunit/TestFixture.h>
36#include <cppunit/extensions/HelperMacros.h>
37
38#include <algorithm>
39#include <fstream>
40#include <regex>
41#include <sstream>
42
43#ifdef PLATFORM_WINDOWS
44#include <cstdio>
45#endif
46
47#ifdef PLATFORM_UNIX
48#include <sys/fcntl.h>
49#include <sys/types.h>
50#endif
51
52using namespace std;
53using namespace CppUtilities;
54using namespace CppUtilities::Literals;
55using namespace CPPUNIT_NS;
56
60class IoTests : public TestFixture {
61 CPPUNIT_TEST_SUITE(IoTests);
62 CPPUNIT_TEST(testBinaryReader);
63 CPPUNIT_TEST(testBinaryWriter);
64 CPPUNIT_TEST(testBitReader);
65 CPPUNIT_TEST(testBufferSearch);
66 CPPUNIT_TEST(testPathUtilities);
67 CPPUNIT_TEST(testIniFile);
68 CPPUNIT_TEST(testAdvancedIniFile);
69 CPPUNIT_TEST(testCopy);
70 CPPUNIT_TEST(testCopyWithNativeFileStream);
71 CPPUNIT_TEST(testReadFile);
72 CPPUNIT_TEST(testWriteFile);
73 CPPUNIT_TEST(testAnsiEscapeCodes);
74#ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
75 CPPUNIT_TEST(testNativeFileStream);
76#endif
77 CPPUNIT_TEST_SUITE_END();
78
79public:
80 void setUp() override;
81 void tearDown() override;
82
83 void testBinaryReader();
84 void testBinaryWriter();
85 void testBitReader();
86 void testBufferSearch();
87 void testPathUtilities();
88 void testIniFile();
90 void testCopy();
92 void testReadFile();
93 void testWriteFile();
95#ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
97#endif
98};
99
101
103{
104}
105
107{
108}
109
114{
115 // read test file
116 fstream testFile;
117 testFile.exceptions(ios_base::failbit | ios_base::badbit);
118 testFile.open(testFilePath("some_data"), ios_base::in | ios_base::binary);
120 CPPUNIT_ASSERT_EQUAL(static_cast<istream::pos_type>(398), reader.readStreamsize());
121 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(0x0102u), reader.readUInt16LE());
122 CPPUNIT_ASSERT_EQUAL(static_cast<istream::pos_type>(396), reader.readRemainingBytes());
123 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint16_t>(0x0102u), reader.readUInt16BE());
124 CPPUNIT_ASSERT_EQUAL(0x010203u, reader.readUInt24LE());
125 CPPUNIT_ASSERT_EQUAL(0x010203u, reader.readUInt24BE());
126 CPPUNIT_ASSERT_EQUAL(0x01020304u, reader.readUInt32LE());
127 CPPUNIT_ASSERT_EQUAL(0x01020304u, reader.readUInt32BE());
128 CPPUNIT_ASSERT_EQUAL(0x0102030405u, reader.readUInt40LE());
129 CPPUNIT_ASSERT_EQUAL(0x0102030405u, reader.readUInt40BE());
130 CPPUNIT_ASSERT_EQUAL(0x01020304050607u, reader.readUInt56LE());
131 CPPUNIT_ASSERT_EQUAL(0x01020304050607u, reader.readUInt56BE());
132 CPPUNIT_ASSERT_EQUAL(0x0102030405060708u, reader.readUInt64LE());
133 CPPUNIT_ASSERT_EQUAL(0x0102030405060708u, reader.readUInt64BE());
134 testFile.seekg(0);
135 CPPUNIT_ASSERT_EQUAL(reader.readInt16LE(), static_cast<std::int16_t>(0x0102));
136 CPPUNIT_ASSERT_EQUAL(reader.readInt16BE(), static_cast<std::int16_t>(0x0102));
137 CPPUNIT_ASSERT_EQUAL(0x010203, reader.readInt24LE());
138 CPPUNIT_ASSERT_EQUAL(0x010203, reader.readInt24BE());
139 CPPUNIT_ASSERT_EQUAL(0x01020304, reader.readInt32LE());
140 CPPUNIT_ASSERT_EQUAL(0x01020304, reader.readInt32BE());
141 CPPUNIT_ASSERT_EQUAL(0x0102030405, reader.readInt40LE());
142 CPPUNIT_ASSERT_EQUAL(0x0102030405, reader.readInt40BE());
143 CPPUNIT_ASSERT_EQUAL(0x01020304050607, reader.readInt56LE());
144 CPPUNIT_ASSERT_EQUAL(0x01020304050607, reader.readInt56BE());
145 CPPUNIT_ASSERT_EQUAL(0x0102030405060708, reader.readInt64LE());
146 CPPUNIT_ASSERT_EQUAL(0x0102030405060708, reader.readInt64BE());
147 CPPUNIT_ASSERT_EQUAL(1.125f, reader.readFloat32LE());
148 CPPUNIT_ASSERT_EQUAL(1.625, reader.readFloat64LE());
149 CPPUNIT_ASSERT_EQUAL(1.125f, reader.readFloat32BE());
150 CPPUNIT_ASSERT_EQUAL(reader.readFloat64BE(), 1.625);
151 CPPUNIT_ASSERT_EQUAL(false, reader.readBool());
152 CPPUNIT_ASSERT_EQUAL(true, reader.readBool());
153 CPPUNIT_ASSERT_EQUAL("abc"s, reader.readString(3));
154 CPPUNIT_ASSERT_EQUAL(reader.readLengthPrefixedString(), "ABC"s);
155 CPPUNIT_ASSERT_EQUAL("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901"
156 "23456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123"
157 "45678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345"
158 "678901234567890123456789"s,
159 reader.readLengthPrefixedString());
160 CPPUNIT_ASSERT_EQUAL("def"s, reader.readTerminatedString());
161 testFile.seekg(-4, ios_base::cur);
162 CPPUNIT_ASSERT_EQUAL("def"s, reader.readTerminatedString(5, 0));
163 CPPUNIT_ASSERT_THROW(reader.readLengthPrefixedString(), ConversionException);
164 CPPUNIT_ASSERT_MESSAGE("pos in stream not advanced on conversion error", reader.readByte() == 0);
165
166 // test ownership
167 reader.setStream(nullptr, true);
168 reader.setStream(new fstream(), true);
170 CPPUNIT_ASSERT(reader2.stream() == reader.stream());
171 CPPUNIT_ASSERT(!reader2.hasOwnership());
172 reader.setStream(&testFile, false);
173 reader.setStream(new fstream(), true);
174}
175
180{
181 // prepare reading expected data
182 fstream testFile;
183 testFile.exceptions(ios_base::failbit | ios_base::badbit);
184 testFile.open(testFilePath("some_data"), ios_base::in | ios_base::binary);
185
186 // prepare output stream
187 stringstream outputStream(ios_base::in | ios_base::out | ios_base::binary);
188 outputStream.exceptions(ios_base::failbit | ios_base::badbit);
189 char testData[397];
190#if defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION)
191#define USE_RDBUF_DIRECTLY
192 outputStream.rdbuf()->pubsetbuf(testData, sizeof(testData));
193#endif
194
195 // write test data
197 writer.writeUInt16LE(0x0102u);
198 writer.writeUInt16BE(0x0102u);
199 writer.writeUInt24LE(0x010203u);
200 writer.writeUInt24BE(0x010203u);
201 writer.writeUInt32LE(0x01020304u);
202 writer.writeUInt32BE(0x01020304u);
203 writer.writeUInt40LE(0x0102030405u);
204 writer.writeUInt40BE(0x0102030405u);
205 writer.writeUInt56LE(0x01020304050607u);
206 writer.writeUInt56BE(0x01020304050607u);
207 writer.writeUInt64LE(0x0102030405060708u);
208 writer.writeUInt64BE(0x0102030405060708u);
209
210#ifndef USE_RDBUF_DIRECTLY
211 outputStream.seekg(0);
212 outputStream.read(testData, 58);
213#endif
214
215 // test written values
216 for (char c : testData) {
217 const auto pos = static_cast<std::size_t>(testFile.tellg());
218 if (pos >= 58) {
219 break;
220 }
221 char expected;
222 testFile.read(&expected, 1);
224 }
225 testFile.seekg(0);
226 outputStream.seekp(0);
227
228 // write more test data
229 writer.writeInt16LE(0x0102);
230 writer.writeInt16BE(0x0102);
231 writer.writeInt24LE(0x010203);
232 writer.writeInt24BE(0x010203);
233 writer.writeInt32LE(0x01020304);
234 writer.writeInt32BE(0x01020304);
235 writer.writeInt40LE(0x0102030405);
236 writer.writeInt40BE(0x0102030405);
237 writer.writeInt56LE(0x01020304050607);
238 writer.writeInt56BE(0x01020304050607);
239 writer.writeInt64LE(0x0102030405060708);
240 writer.writeInt64BE(0x0102030405060708);
241 writer.writeFloat32LE(1.125);
242 writer.writeFloat64LE(1.625);
243 writer.writeFloat32BE(1.125);
244 writer.writeFloat64BE(1.625);
245 writer.writeBool(false);
246 writer.writeBool(true);
247 writer.writeString("abc");
248 writer.writeLengthPrefixedString("ABC");
249 writer.writeLengthPrefixedString("012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
250 "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901"
251 "234567890123456789012345678901234567890123456789012345678901234567890123456789");
252 writer.writeTerminatedString("def");
253
254#ifndef USE_RDBUF_DIRECTLY
255 outputStream.seekg(0);
256 outputStream.read(testData, 58);
257#endif
258
259 // test written values
260 for (char c : testData) {
261 const auto pos = static_cast<std::size_t>(testFile.tellg());
262 if (pos >= 58) {
263 break;
264 }
265 char expected;
266 testFile.read(&expected, 1);
268 }
269
270 // test ownership
271 writer.setStream(nullptr, true);
272 writer.setStream(new fstream(), true);
274 CPPUNIT_ASSERT(writer2.stream() == writer.stream());
275 CPPUNIT_ASSERT(!writer2.hasOwnership());
276 writer.setStream(&testFile, false);
277 writer.setStream(new fstream(), true);
278}
279
284{
285 const std::uint8_t testData[] = { 0x81, 0x90, 0x3C, 0x44, 0x28, 0x00, 0x44, 0x10, 0x20, 0xFF, 0xFA };
286 BitReader reader(reinterpret_cast<const char *>(testData), sizeof(testData));
287 CPPUNIT_ASSERT(reader.readBit() == 1);
288 reader.skipBits(6);
289 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(3), reader.showBits<std::uint8_t>(2));
290 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(3), reader.readBits<std::uint8_t>(2));
291 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint32_t>(0x103C4428 << 1), reader.readBits<std::uint32_t>(32));
292 reader.align();
293 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(0x44), reader.readBits<std::uint8_t>(8));
294 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(7), reader.readUnsignedExpGolombCodedBits<std::uint8_t>());
295 CPPUNIT_ASSERT_EQUAL(static_cast<std::int8_t>(4), reader.readSignedExpGolombCodedBits<std::int8_t>());
296 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(0), reader.readBit());
297 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(0), reader.readBit());
298 reader.skipBits(8 + 4);
299 CPPUNIT_ASSERT_EQUAL(4_st, reader.bitsAvailable());
300 CPPUNIT_ASSERT_EQUAL(static_cast<std::uint8_t>(0xA), reader.readBits<std::uint8_t>(4));
301 CPPUNIT_ASSERT_THROW(reader.readBit(), std::ios_base::failure);
302 CPPUNIT_ASSERT_THROW(reader.skipBits(1), std::ios_base::failure);
303 reader.reset(reinterpret_cast<const char *>(testData), sizeof(testData));
304 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(8 * sizeof(testData)), reader.bitsAvailable());
305}
306
311{
312 // setup search to test
313 auto expectedResult = std::string();
314 auto hasResult = false;
315 auto bs = BufferSearch("Updated version: ", "\t\n", "Starting build", [&](BufferSearch &, std::string &&result) {
317 CPPUNIT_ASSERT_MESSAGE("callback only invoked once", !hasResult);
318 hasResult = true;
319 });
320
321 // feed data into the search
322 char buffer[30] = { 0 };
323 bs(buffer, 0);
325 std::strcpy(buffer, "Starting Updated");
326 bs(std::string_view(buffer, 16));
328 std::strcpy(buffer, " version: some ");
329 bs(buffer, 15);
331 expectedResult = "some version number";
332 std::strcpy(buffer, "version number\tmore chars");
333 bs(buffer, 25);
335 hasResult = false;
336 std::strcpy(buffer, "... Starting build ...");
337 bs(buffer, 22);
339}
340
345{
346 CPPUNIT_ASSERT_EQUAL("libc++utilities.so"s, fileName("C:\\libs\\libc++utilities.so"));
347 CPPUNIT_ASSERT_EQUAL("libc++utilities.so"s, fileName("C:\\libs/libc++utilities.so"));
348 CPPUNIT_ASSERT_EQUAL("libc++utilities.so"s, fileName("/usr/lib/libc++utilities.so"));
349 CPPUNIT_ASSERT_EQUAL("libc++utilities.so"s, fileName("libc++utilities.so"));
350 CPPUNIT_ASSERT_EQUAL("/usr/lib/"s, directory("/usr/lib/libc++utilities.so"));
351 CPPUNIT_ASSERT_EQUAL(string(), directory("libc++utilities.so"));
352 CPPUNIT_ASSERT_EQUAL("C:\\libs\\"s, directory("C:\\libs\\libc++utilities.so"));
353 CPPUNIT_ASSERT_EQUAL("C:\\libs/"s, directory("C:\\libs/libc++utilities.so"));
354 string invalidPath("lib/c++uti*lities.so?");
356 CPPUNIT_ASSERT(invalidPath == "libc++utilities.so");
357
358 const auto input = std::string_view("some/path/täst");
359 const auto expected = input;
360 const auto output = makeNativePath(input);
361#ifdef PLATFORM_WINDOWS
362 const auto outputAsUtf8 = convertUtf16LEToUtf8(reinterpret_cast<const char *>(output.data()), output.size() * 2);
363 const auto outputView = std::string_view(outputAsUtf8.first.get(), outputAsUtf8.second);
365#else
366 CPPUNIT_ASSERT_EQUAL_MESSAGE("makeNativePath()", expected, output);
367#endif
368}
369
374{
375 // prepare reading test file
376 fstream inputFile;
377 inputFile.exceptions(ios_base::failbit | ios_base::badbit);
378 inputFile.open(testFilePath("test.ini"), ios_base::in);
379
380 IniFile ini;
382 const auto globalScope = ini.data().at(0);
383 const auto scope1 = ini.data().at(1);
384 const auto scope2 = ini.data().at(2);
385 CPPUNIT_ASSERT(globalScope.first.empty());
386 CPPUNIT_ASSERT(globalScope.second.find("key0") != globalScope.second.cend());
387 CPPUNIT_ASSERT(globalScope.second.find("key0")->second == "value 0");
388 CPPUNIT_ASSERT(globalScope.second.find("key1") == globalScope.second.cend());
389 CPPUNIT_ASSERT(scope1.first == "scope 1");
390 CPPUNIT_ASSERT(scope1.second.find("key1") != scope1.second.cend());
391 CPPUNIT_ASSERT(scope1.second.find("key1")->second == "value 1");
392 CPPUNIT_ASSERT(scope1.second.find("key2") != scope1.second.cend());
393 CPPUNIT_ASSERT(scope1.second.find("key2")->second == "value=2");
394 CPPUNIT_ASSERT(scope2.first == "scope 2");
395 CPPUNIT_ASSERT(scope2.second.find("key5") == scope2.second.cend());
396
397 // write values to another file
398 fstream outputFile;
399 outputFile.exceptions(ios_base::failbit | ios_base::badbit);
400 outputFile.open(workingCopyPath("output.ini", WorkingCopyMode::NoCopy), ios_base::out | ios_base::trunc);
401 ini.make(outputFile);
402
403 // parse written values (again)
404 outputFile.close();
405 outputFile.open(workingCopyPath("output.ini", WorkingCopyMode::NoCopy), ios_base::in);
408 CPPUNIT_ASSERT(ini.data() == ini2.data());
409}
410
415{
416 // prepare reading test file
417 fstream inputFile;
418 inputFile.exceptions(ios_base::failbit | ios_base::badbit);
419 inputFile.open(testFilePath("pacman.conf"), ios_base::in);
420
421 // parse the test file
424
425 // check whether scope data is as expected
426 CPPUNIT_ASSERT_EQUAL_MESSAGE("5 scopes (taking implicit empty section at the end into account)", 5_st, ini.sections.size());
427 auto options = ini.findSection("options");
428 CPPUNIT_ASSERT(options != ini.sectionEnd());
429#if !defined(PLATFORM_WINDOWS) || defined(PLATFORM_MINGW) || defined(PLATFORM_CYGWIN)
430#define STD_REGEX_WORKS // the parsed data looks good, apparently std::regex behaves differently on MSVC
432 "comment block before section", "# Based on.*\n.*# GENERAL OPTIONS\n#\n"s, std::regex::extended, options->precedingCommentBlock);
433#endif
434 CPPUNIT_ASSERT_EQUAL(7_st, options->fields.size());
435 CPPUNIT_ASSERT_EQUAL("HoldPkg"s, options->fields[0].key);
436 CPPUNIT_ASSERT_EQUAL("pacman glibc"s, options->fields[0].value);
437 CPPUNIT_ASSERT_MESSAGE("value present", options->fields[0].flags & IniFileFieldFlags::HasValue);
438#ifdef STD_REGEX_WORKS
439 TESTUTILS_ASSERT_LIKE_FLAGS("comment block between section header and first field",
440 "# The following paths are.*\n.*#HookDir = /etc/pacman\\.d/hooks/\n"s, std::regex::extended, options->fields[0].precedingCommentBlock);
441#endif
442 CPPUNIT_ASSERT_EQUAL(""s, options->fields[0].followingInlineComment);
443 CPPUNIT_ASSERT_EQUAL("Foo"s, options->fields[1].key);
444 CPPUNIT_ASSERT_EQUAL("bar"s, options->fields[1].value);
445 CPPUNIT_ASSERT_MESSAGE("value present", options->fields[1].flags & IniFileFieldFlags::HasValue);
446#ifdef STD_REGEX_WORKS
447 TESTUTILS_ASSERT_LIKE_FLAGS("comment block between fields", "#XferCommand.*\n.*#CleanMethod = KeepInstalled\n"s, std::regex::extended,
448 options->fields[1].precedingCommentBlock);
449#endif
450 CPPUNIT_ASSERT_EQUAL("# inline comment"s, options->fields[1].followingInlineComment);
451 CPPUNIT_ASSERT_EQUAL("CheckSpace"s, options->fields[3].key);
452 CPPUNIT_ASSERT_EQUAL(""s, options->fields[3].value);
453 CPPUNIT_ASSERT_MESSAGE("no value present", !(options->fields[3].flags & IniFileFieldFlags::HasValue));
454#ifdef STD_REGEX_WORKS
455 TESTUTILS_ASSERT_LIKE_FLAGS("empty lines in comments preserved", "\n# Pacman.*\n.*\n\n#NoUpgrade =\n.*#TotalDownload\n"s, std::regex::extended,
456 options->fields[3].precedingCommentBlock);
457#endif
458 CPPUNIT_ASSERT_EQUAL(""s, options->fields[3].followingInlineComment);
459 auto extraScope = ini.findSection(options, "extra");
460 CPPUNIT_ASSERT(extraScope != ini.sectionEnd());
461 CPPUNIT_ASSERT_EQUAL_MESSAGE("comment block which is only an empty line", "\n"s, extraScope->precedingCommentBlock);
462 CPPUNIT_ASSERT_EQUAL_MESSAGE("inline comment after scope", "# an inline comment after a scope name"s, extraScope->followingInlineComment);
463 CPPUNIT_ASSERT_EQUAL(1_st, extraScope->fields.size());
464 CPPUNIT_ASSERT(ini.sections.back().flags & IniFileSectionFlags::Implicit);
465#ifdef STD_REGEX_WORKS
466 TESTUTILS_ASSERT_LIKE_FLAGS("comment block after last field present in implicitly added last scope", "\n# If you.*\n.*custompkgs\n"s,
467 std::regex::extended, ini.sections.back().precedingCommentBlock);
468#endif
469
470 // test finding a field from file level and const access
471 const auto *const constIniFile = &ini;
472 auto includeField = constIniFile->findField("extra", "Include");
473 CPPUNIT_ASSERT(includeField.has_value());
474 CPPUNIT_ASSERT_EQUAL("Include"s, includeField.value()->key);
475 CPPUNIT_ASSERT_EQUAL("/etc/pacman.d/mirrorlist"s, includeField.value()->value);
476 CPPUNIT_ASSERT_MESSAGE("field not present", !constIniFile->findField("extra", "Includ").has_value());
477 CPPUNIT_ASSERT_MESSAGE("scope not present", !constIniFile->findField("extr", "Includ").has_value());
478
479 // write values again; there shouldn't be a difference as the parser and the writer are supposed to
480 // preserve the order of all elements and comments
481 std::stringstream newFile;
482 ini.make(newFile);
483 std::string originalContents;
484 inputFile.clear();
485 inputFile.seekg(std::ios_base::beg);
486 // ignore warning about null pointer dereference from GCC 12 for now (which is *likely* not correct)
487#ifdef __GNUC__
488#pragma GCC diagnostic push
489#pragma GCC diagnostic ignored "-Wnull-dereference"
490#endif
492#ifdef __GNUC__
493#pragma GCC diagnostic pop
494#endif
496}
497
502{
503 // prepare streams
504 auto testFile = std::fstream();
505 testFile.exceptions(ios_base::failbit | ios_base::badbit);
506 testFile.open(testFilePath("some_data"), ios_base::in | ios_base::binary);
507 auto outputStream = std::stringstream(ios_base::in | ios_base::out | ios_base::binary);
508 outputStream.exceptions(ios_base::failbit | ios_base::badbit);
509
510 // copy
511 auto copyHelper = CopyHelper<13>();
513 CPPUNIT_ASSERT_EQUAL(static_cast<std::fstream::pos_type>(0), outputStream.tellg());
514 CPPUNIT_ASSERT_EQUAL(static_cast<std::fstream::pos_type>(50), outputStream.tellp());
515 CPPUNIT_ASSERT_EQUAL(static_cast<std::fstream::pos_type>(50), testFile.tellg());
516 CPPUNIT_ASSERT_EQUAL(static_cast<std::fstream::pos_type>(50), testFile.tellp());
517
518 // verify
519 testFile.seekg(0);
520 for (auto i = 0; i < 50; ++i) {
522 }
523}
524
529{
530 // prepare streams
531 auto testFile = NativeFileStream();
532 testFile.exceptions(ios_base::failbit | ios_base::badbit);
533 testFile.open(testFilePath("some_data"), ios_base::in | ios_base::binary);
534 auto outputPath = workingCopyPath("copied_data", WorkingCopyMode::Cleanup);
536 outputStream.exceptions(ios_base::failbit | ios_base::badbit);
537 outputStream.open(outputPath, ios_base::out | ios_base::binary);
538
539 // copy
540 auto copyHelper = CopyHelper<13>();
542 CPPUNIT_ASSERT(outputStream.is_open());
543 CPPUNIT_ASSERT(testFile.is_open());
544
545 // test seek positions (the expected values are from a run with normal std::fstream)
546 CPPUNIT_ASSERT_EQUAL(static_cast<std::fstream::pos_type>(50), outputStream.tellg());
547 CPPUNIT_ASSERT_EQUAL(static_cast<std::fstream::pos_type>(50), outputStream.tellp());
548 CPPUNIT_ASSERT_EQUAL(static_cast<std::fstream::pos_type>(50), testFile.tellg());
549 CPPUNIT_ASSERT_EQUAL(static_cast<std::fstream::pos_type>(50), testFile.tellp());
550
551 // add a few more bytes to output stream (to be sure it is still usable) and re-open for reading
552 const auto aFewMoreBytes = "a few more bytes"sv;
554 outputStream.close();
555 outputStream.open(outputPath, ios_base::in | ios_base::binary);
556
557 // verify
558 testFile.seekg(0);
559 for (auto i = 0; i < 50; ++i) {
561 }
562 auto tail = std::string(aFewMoreBytes.size(), '0');
563 outputStream.read(tail.data(), static_cast<std::streamsize>(tail.size()));
564 CPPUNIT_ASSERT_EQUAL(aFewMoreBytes, std::string_view(tail.data(), tail.size()));
565
566 // repeat with callback version
567 auto percentage = 0.0;
568 const auto isAborted = [] { return false; };
569 const auto callback = [&percentage](double p) { percentage = p; };
570 testFile.seekg(0);
571 outputStream.close();
572 outputStream.open(outputPath, ios_base::out | ios_base::trunc | ios_base::binary);
573 copyHelper.callbackCopy(testFile, outputStream, 50, isAborted, callback);
575
576 // verify again
577 CPPUNIT_ASSERT(outputStream.is_open());
578 CPPUNIT_ASSERT(testFile.is_open());
579 CPPUNIT_ASSERT_EQUAL(static_cast<std::fstream::pos_type>(50), outputStream.tellg());
580 CPPUNIT_ASSERT_EQUAL(static_cast<std::fstream::pos_type>(50), outputStream.tellp());
581 CPPUNIT_ASSERT_EQUAL(static_cast<std::fstream::pos_type>(50), testFile.tellg());
582 CPPUNIT_ASSERT_EQUAL(static_cast<std::fstream::pos_type>(50), testFile.tellp());
584 outputStream.close();
585 outputStream.open(outputPath, ios_base::in | ios_base::binary);
586 testFile.seekg(0);
587 for (auto i = 0; i < 50; ++i) {
589 }
590 tail.assign(aFewMoreBytes.size(), '0');
591 outputStream.read(tail.data(), static_cast<std::streamsize>(tail.size()));
592 CPPUNIT_ASSERT_EQUAL(aFewMoreBytes, std::string_view(tail.data(), tail.size()));
593}
594
599{
600 // read a file successfully
601 const string iniFilePath(testFilePath("test.ini"));
602 CPPUNIT_ASSERT_EQUAL("# file for testing INI parser\n"
603 "key0=value 0\n"
604 "\n"
605 "[scope 1]\n"
606 "key1=value 1 # comment\n"
607 "key2=value=2\n"
608 "key3=value 3\n"
609 "\n"
610 "[scope 2]\n"
611 "key4=value 4\n"
612 "#key5=value 5\n"
613 "key6=value 6\n"s,
615
616 // fail by exceeding max size
617 CPPUNIT_ASSERT_THROW(readFile(iniFilePath, 10), std::ios_base::failure);
618
619 // handle UTF-8 in path and file contents correctly via NativeFileStream
620#if !defined(PLATFORM_WINDOWS) || defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER)
621 CPPUNIT_ASSERT_EQUAL("file with non-ASCII character 'ä' in its name\n"s, readFile(testFilePath("täst.txt")));
622#endif
623}
624
629{
630 const string path(workingCopyPath("test.ini", WorkingCopyMode::NoCopy));
631 writeFile(path, "some contents");
632 CPPUNIT_ASSERT_EQUAL("some contents"s, readFile(path));
633}
634
639{
640 stringstream ss1;
642 ss1 << EscapeCodes::Phrases::Error << "some error" << EscapeCodes::Phrases::End;
643 ss1 << EscapeCodes::Phrases::Warning << "some warning" << EscapeCodes::Phrases::End;
644 ss1 << EscapeCodes::Phrases::Info << "some info" << EscapeCodes::Phrases::End;
645 ss1 << EscapeCodes::Phrases::ErrorMessage << "Arch-style error" << EscapeCodes::Phrases::End;
646 ss1 << EscapeCodes::Phrases::WarningMessage << "Arch-style warning" << EscapeCodes::Phrases::End;
647 ss1 << EscapeCodes::Phrases::PlainMessage << "Arch-style message" << EscapeCodes::Phrases::End;
648 ss1 << EscapeCodes::Phrases::SuccessMessage << "Arch-style success" << EscapeCodes::Phrases::End;
649 ss1 << EscapeCodes::Phrases::SubMessage << "Arch-style sub-message" << EscapeCodes::Phrases::End;
650 ss1 << EscapeCodes::color(EscapeCodes::Color::Blue, EscapeCodes::Color::Red, EscapeCodes::TextAttribute::Blink)
651 << "blue, blinking text on red background" << EscapeCodes::TextAttribute::Reset << '\n';
652 cout << "\noutput for formatting with ANSI escape codes:\n" << ss1.str() << "---------------------------------------------\n";
653 CPPUNIT_ASSERT_EQUAL("\033[1;31mError: \033[0m\033[1msome error\033[0m\n"
654 "\033[1;33mWarning: \033[0m\033[1msome warning\033[0m\n"
655 "\033[1;34mInfo: \033[0m\033[1msome info\033[0m\n"
656 "\033[1;31m==> ERROR: \033[0m\033[1mArch-style error\033[0m\n"
657 "\033[1;33m==> WARNING: \033[0m\033[1mArch-style warning\033[0m\n"
658 " \033[0m\033[1mArch-style message\033[0m\n"
659 "\033[1;32m==> \033[0m\033[1mArch-style success\033[0m\n"
660 "\033[1;32m -> \033[0m\033[1mArch-style sub-message\033[0m\n"
661 "\033[5;34;41mblue, blinking text on red background\033[0m\n"s,
662 ss1.str());
663
664 stringstream ss2;
665 EscapeCodes::enabled = false;
666 ss2 << EscapeCodes::Phrases::Info << "some info" << EscapeCodes::Phrases::End;
667 CPPUNIT_ASSERT_EQUAL("Info: some info\n"s, ss2.str());
668}
669
670#ifdef CPP_UTILITIES_USE_NATIVE_FILE_BUFFER
674void IoTests::testNativeFileStream()
675{
676 return;
677 // open file by path
678 const auto txtFilePath(workingCopyPath("täst.txt"));
680 fileStream.exceptions(ios_base::badbit | ios_base::failbit);
681 CPPUNIT_ASSERT(!fileStream.is_open());
682 fileStream.open(txtFilePath, ios_base::in);
683 CPPUNIT_ASSERT(fileStream.is_open());
684#if defined(PLATFORM_WINDOWS) && defined(CPP_UTILITIES_USE_BOOST_IOSTREAMS)
685 CPPUNIT_ASSERT(fileStream.fileHandle() != nullptr);
686#else
687 CPPUNIT_ASSERT(fileStream.fileDescriptor() != -1);
688#endif
689 CPPUNIT_ASSERT_EQUAL(static_cast<char>(fileStream.get()), 'f');
690 fileStream.seekg(0, ios_base::end);
691 CPPUNIT_ASSERT_EQUAL(fileStream.tellg(), static_cast<NativeFileStream::pos_type>(47));
692 fileStream.close();
693 CPPUNIT_ASSERT(!fileStream.is_open());
694 try {
695 fileStream.open("non existing file", ios_base::in | ios_base::out | ios_base::binary);
696 CPPUNIT_FAIL("expected exception");
697 } catch (const std::ios_base::failure &failure) {
698#ifdef PLATFORM_WINDOWS
699#ifdef CPP_UTILITIES_USE_BOOST_IOSTREAMS
700 TESTUTILS_ASSERT_LIKE("expected error with some message", "CreateFileW failed: .+", failure.what());
701#else
702 TESTUTILS_ASSERT_LIKE("expected error with some message", "_wopen failed: .+", failure.what());
703#endif
704#else
705 TESTUTILS_ASSERT_LIKE("expected error with some message", "open failed: .+", failure.what());
706#endif
707 }
708 fileStream.clear();
709
710 // open file from file descriptor
711#ifndef PLATFORM_WINDOWS
714 fileStream.open(readWriteFileDescriptor, ios_base::in | ios_base::out | ios_base::binary);
715 CPPUNIT_ASSERT(fileStream.is_open());
716 CPPUNIT_ASSERT_EQUAL(static_cast<char>(fileStream.get()), 'f');
717 fileStream.seekg(0, ios_base::end);
718 CPPUNIT_ASSERT_EQUAL(fileStream.tellg(), static_cast<NativeFileStream::pos_type>(47));
719 fileStream.flush();
720 fileStream.close();
721 CPPUNIT_ASSERT(!fileStream.is_open());
722#endif
723 try {
724 fileStream.open(-1, ios_base::in | ios_base::out | ios_base::binary);
725 fileStream.get();
726 CPPUNIT_FAIL("expected exception");
727 } catch (const std::ios_base::failure &failure) {
728#ifndef PLATFORM_WINDOWS
730 "expected error message", "(basic_ios::clear|failed reading: Bad file descriptor): iostream error"s, string(failure.what()));
731#else
732 CPP_UTILITIES_UNUSED(failure)
733#endif
734 }
735 fileStream.clear();
736
737 // append + write file via path
739 fileStream2.exceptions(ios_base::failbit | ios_base::badbit);
740 fileStream2.open(txtFilePath, ios_base::in | ios_base::out | ios_base::app);
741 CPPUNIT_ASSERT(fileStream2.is_open());
742 fileStream2 << "foo";
743 fileStream2.flush();
744 fileStream2.close();
745 CPPUNIT_ASSERT(!fileStream2.is_open());
746 CPPUNIT_ASSERT_EQUAL("file with non-ASCII character 'ä' in its name\nfoo"s, readFile(txtFilePath, 50));
747
748 // truncate + write file via path
749 fileStream2.open(txtFilePath, ios_base::out | ios_base::trunc);
750 CPPUNIT_ASSERT(fileStream2.is_open());
751 fileStream2 << "bar";
752 fileStream2.close();
753 CPPUNIT_ASSERT(!fileStream2.is_open());
755
756 // append + write via file descriptor from file handle
757#ifdef PLATFORM_WINDOWS
758 const auto wideTxtFilePath = NativeFileStream::makeWidePath(txtFilePath);
759 const auto appendFileHandle = _wfopen(wideTxtFilePath.get(), L"a+");
760#else
761 const auto appendFileHandle = fopen(txtFilePath.data(), "a");
762#endif
764 fileStream2.open(fileno(appendFileHandle), ios_base::out | ios_base::app);
765 CPPUNIT_ASSERT(fileStream2.is_open());
766 fileStream2 << "foo";
767 fileStream2.close();
768 CPPUNIT_ASSERT(!fileStream2.is_open());
770}
771#endif
#define CPP_UTILITIES_UNUSED(x)
Prevents warnings about unused variables.
Definition global.h:92
Reads primitive data types from a std::istream.
Writes primitive data types to a std::ostream.
The BitReader class provides bitwise reading of buffered data.
Definition bitreader.h:13
The BufferSearch struct invokes a callback if an initially given search term occurs in consecutively ...
The ConversionException class is thrown by the various conversion functions of this library when a co...
The IniFile class allows parsing and writing INI files.
Definition inifile.h:16
void parse(std::istream &inputStream)
Parses all data from the specified inputStream.
Definition inifile.cpp:40
The IoTests class tests classes and functions provided by the files inside the io directory.
Definition iotests.cpp:60
void testBufferSearch()
Tests the BufferSearch class.
Definition iotests.cpp:310
void testBinaryReader()
Tests the most important methods of the BinaryReader.
Definition iotests.cpp:113
void testAnsiEscapeCodes()
Tests formatting functions of CppUtilities::EscapeCodes namespace.
Definition iotests.cpp:638
void testIniFile()
Tests IniFile.
Definition iotests.cpp:373
void testBitReader()
Tests the BitReader class.
Definition iotests.cpp:283
void testBinaryWriter()
Tests the most important methods of the BinaryWriter.
Definition iotests.cpp:179
void testCopyWithNativeFileStream()
Tests CopyHelper in combination with NativeFileStream.
Definition iotests.cpp:528
void setUp() override
Definition iotests.cpp:102
void testCopy()
Tests CopyHelper.
Definition iotests.cpp:501
void testPathUtilities()
Tests fileName() and removeInvalidChars().
Definition iotests.cpp:344
void tearDown() override
Definition iotests.cpp:106
void testWriteFile()
Tests writeFile().
Definition iotests.cpp:628
void testReadFile()
Tests readFile().
Definition iotests.cpp:598
void testAdvancedIniFile()
Tests AdvancedIniFile.
Definition iotests.cpp:414
CPPUNIT_TEST_SUITE_REGISTRATION(IoTests)
constexpr auto color(Color foreground, Color background, TextAttribute displayAttribute=TextAttribute::Reset)
CPP_UTILITIES_EXPORT bool enabled
Controls whether the functions inside the EscapeCodes namespace actually make use of escape codes.
Contains literals to ease asserting with CPPUNIT_ASSERT_EQUAL.
Definition testutils.h:368
Contains all utilities provides by the c++utilities library.
CPP_UTILITIES_EXPORT std::string readFile(const std::string &path, std::string::size_type maxSize=std::string::npos)
Reads all contents of the specified file in a single call.
Definition misc.cpp:17
CPP_UTILITIES_EXPORT std::string testFilePath(const std::string &relativeTestFilePath)
Convenience function to invoke TestApplication::testFilePath().
Definition testutils.h:161
CPP_UTILITIES_EXPORT std::string workingCopyPath(const std::string &relativeTestFilePath, WorkingCopyMode mode=WorkingCopyMode::CreateCopy)
Convenience function to invoke TestApplication::workingCopyPath().
Definition testutils.h:179
CPP_UTILITIES_EXPORT StringData convertUtf16LEToUtf8(const char *inputBuffer, std::size_t inputBufferSize)
Converts the specified UTF-16 (little-endian) string to UTF-8.
CPP_UTILITIES_EXPORT std::ostream & operator<<(std::ostream &out, Indentation indentation)
IntegralType stringToNumber(const StringType &string, BaseType base=10)
Converts the given string to an unsigned/signed number assuming string uses the specified base.
CPP_UTILITIES_EXPORT StringData convertUtf16BEToUtf8(const char *inputBuffer, std::size_t inputBufferSize)
Converts the specified UTF-16 (big-endian) string to UTF-8.
std::fstream NativeFileStream
StringType argsToString(Args &&...args)
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:258
CPP_UTILITIES_EXPORT void writeFile(std::string_view path, std::string_view contents)
Writes all contents to the specified file in a single call.
Definition misc.cpp:58
CPP_UTILITIES_EXPORT void removeInvalidChars(std::string &fileName)
Removes invalid characters from the specified fileName.
Definition path.cpp:75
CPP_UTILITIES_EXPORT std::string fileName(const std::string &path)
Returns the file name and extension of the specified path string.
Definition path.cpp:17
NativePathStringView makeNativePath(PathStringView path)
Returns path in the platform's native encoding.
Definition path.h:82
CPP_UTILITIES_EXPORT std::string directory(const std::string &path)
Returns the directory of the specified path string (including trailing slash).
Definition path.cpp:25
STL namespace.
The AdvancedIniFile class allows parsing and writing INI files.
Definition inifile.h:79
void parse(std::istream &inputStream, IniFileParseOptions options=IniFileParseOptions::None)
Parses all data from the specified inputStream.
Definition inifile.cpp:217
#define TESTUTILS_ASSERT_LIKE_FLAGS(message, expectedRegex, regexFlags, actualString)
Asserts whether the specified string matches the specified regex.
Definition testutils.h:328
#define TESTUTILS_ASSERT_LIKE(message, expectedRegex, actualString)
Asserts whether the specified string matches the specified regex.
Definition testutils.h:338
constexpr int i