Squashed 'external/mINI/' content from commit 52b66e987c

git-subtree-dir: external/mINI
git-subtree-split: 52b66e987cb56171dc91d96115cdf094b6e4d7a0
This commit is contained in:
irisz64
2025-06-26 23:33:30 +02:00
commit 45cc47a859
26 changed files with 5013 additions and 0 deletions

747
tests/testwrite.cpp Normal file
View File

@@ -0,0 +1,747 @@
#include <iostream>
#include <string>
#include <vector>
#include <tuple>
#include <fstream>
#include "lest.hpp"
#include "mini/ini.h"
using T_LineData = std::vector<std::string>;
using T_INIFileData = std::tuple<std::string, T_LineData, T_LineData>;
//
// helper functions
//
bool writeTestFile(T_INIFileData const& testData)
{
std::string const& filename = std::get<0>(testData);
T_LineData const& lines = std::get<1>(testData);
std::ofstream fileWriteStream(filename);
if (fileWriteStream.is_open())
{
if (lines.size())
{
auto it = lines.begin();
for (;;)
{
fileWriteStream << *it;
if (++it == lines.end())
{
break;
}
fileWriteStream << std::endl;
}
}
return true;
}
return false;
}
bool verifyData(T_INIFileData const& testData)
{
// compares file contents to expected data
std::string line;
std::string const& filename = std::get<0>(testData);
T_LineData const& linesExpected = std::get<2>(testData);
size_t lineCount = 0;
size_t lineCountExpected = linesExpected.size();
std::ifstream fileReadStream(filename);
if (fileReadStream.is_open())
{
while (std::getline(fileReadStream, line))
{
if (fileReadStream.bad())
{
return false;
}
if (lineCount >= lineCountExpected)
{
std::cout << "Line count exceeds expected!" << std::endl;
return false;
}
std::string const& lineExpected = linesExpected[lineCount++];
if (line != lineExpected)
{
std::cout << "Line " << lineCount << " does not match expected!" << std::endl;
std::cout << "Expected: " << lineExpected << std::endl;
std::cout << "Is: " << line << std::endl;
return false;
}
}
if (lineCount < lineCountExpected)
{
std::cout << "Line count falls behind expected!" << std::endl;
}
return lineCount == lineCountExpected;
}
return false;
}
void outputData(mINI::INIStructure const& ini)
{
for (auto const& it : ini)
{
auto const& section = it.first;
auto const& collection = it.second;
std::cout << "[" << section << "]" << std::endl;
for (auto const& it2 : collection)
{
auto const& key = it2.first;
auto const& value = it2.second;
std::cout << key << "=" << value << std::endl;
}
std::cout << std::endl;
}
}
//
// test data
//
const T_INIFileData testDataBasic {
// filename
"data01.ini",
// original data
{
";some comment",
"[some section]",
"some key=1",
"other key=2"
},
// expected result
{
";some comment",
"[some section]",
"some key=2",
"other key=2",
"yet another key=3"
}
};
const T_INIFileData testDataWithGarbage {
// filename
"data02.ini",
// original data
{
"",
" GARBAGE ; ALSO GARBAGE ",
";;;;;;;;;;;;;;;comment comment",
";;;;",
";;;; ",
" ;",
" ;; ;;xxxx ",
"ignored key = ignored value",
"ignored=ignored",
"GARBAGE",
"",
"ignored key2=ignored key2",
"GARBAGE ;;;;;;;;;;;;;;;;;;;;;",
"[section] ; trailing comment",
"",
"GARBAGE",
";;",
"a=1",
"b = 2",
"c =3",
"d= 4",
"e = 5",
"f =6",
"",
"@#%$@(*(!@*@GARBAGE#!@GARBAGE%$@#GARBAGE%@&*%@$",
"GARBAGE",
"[other section] ;also a comment",
"GARBAGE",
"dinosaurs= 16",
"GARBAGE",
"birds= 123456",
"giraffes= 22",
"GARBAGE",
"[extra section];also a comment",
" aaa = 1",
" bbb=2",
" ccc = 3",
"GARBAGE",
"",
""
},
// expected result
{
"",
";;;;;;;;;;;;;;;comment comment",
";;;;",
";;;; ",
" ;",
" ;; ;;xxxx ",
"",
"[section] ; trailing comment",
"",
";;",
"a=2",
"b = 3",
"c =4",
"d= 5",
"e = 6",
"f =7",
"g=8",
"",
"[other section] ;also a comment",
"birds= 123456",
"giraffes= 22",
"[extra section];also a comment",
" aaa = 2",
" bbb=3",
" ccc = 4",
"ddd=5",
"",
"",
"[new section]",
"test=something"
}
};
const T_INIFileData testDataRemEntry {
// filename
"data04.ini",
// original data
{
"[section]",
"data1=A",
"data2=B"
},
// expected result
{
"[section]",
"data2=B"
}
};
const T_INIFileData testDataRemSection {
// filename
"data05.ini",
// original data
{
"[section]",
"data1=A",
"data2=B"
},
// expected result
{
}
};
const T_INIFileData testDataDuplicateKeys {
// filename
"data06.ini",
// original data
{
"[section]",
"data=A",
"data=B",
"[section]",
"data=C"
},
// expected result
{
"[section]",
"data=D",
"data=D",
"[section]",
"data=D"
}
};
const T_INIFileData testDataDuplicateSections {
// filename
"data07.ini",
// original data
{
"[section]",
"[section]",
"[section]"
},
// expected result
{
"[section]",
"data=A",
"[section]",
"data=A",
"[section]",
"data=A"
}
};
const T_INIFileData testDataPrettyPrint {
// filename
"data08.ini",
// oiriginal data
{
"[section1]",
"value1=A",
"value2=B",
"[section2]",
"value1=A"
},
// expected result
{
"[section1]",
"value1=A",
"value2=B",
"value3 = C",
"[section2]",
"value1=A",
"value2 = B",
"",
"[section3]",
"value1 = A",
"value2 = B"
}
};
const T_INIFileData testDataEmptyFile {
// filename
"data09.ini",
// original data
{},
// expected results
{
"[section]",
"key=value"
}
};
const T_INIFileData testDataEmptySection {
// filename
"data10.ini",
// original data
{
"[section]"
},
// expected result
{
"[section]",
"key=value"
}
};
const T_INIFileData testDataManyEmptySections {
// filename
"data11.ini",
// original data
{
"[section1]",
"[section2]",
"[section3]",
"[section4]",
"[section5]"
},
// expected result
{
"[section1]",
"[section2]",
"[section3]",
"key=value",
"[section4]",
"[section5]"
}
};
const T_INIFileData testDataEmptyNames {
// filename
"data12.ini",
// original data
{
"[]",
"key=value",
"key2=value2",
"[section]",
"=value"
},
// expected result
{
"[]",
"key=",
"=value3",
"[section]",
"=value2",
"[section2]",
"="
}
};
const T_INIFileData testDataMalformed1 {
// filename
"data13.ini",
// original data
{
"[[name1]",
"key=value",
"[name2]]",
"key=value",
"[[name3]]",
"key=value"
},
// expected result
{
"[[name1]",
"key=value1",
"[name2]]",
"key=value2",
"[[name3]]",
"key=value3"
}
};
const T_INIFileData testDataMalformed2 {
// filename
"data14.ini",
// original data
{
"[name]",
"\\===", // key: "=" value: "="
"a\\= \\===b", // key: "a= =" value: "=b"
"c\\= \\===d" // key: "c= =" value: "=d"
},
// expected result
{
"[name]",
"\\====", // key: "=" value: "=="
"a\\= \\===bb", // key: "a= =" value: "=bb"
"e\\===f=", // key: "e=" value: "=f=",
"[other]",
"\\==="
}
};
const T_INIFileData testDataConsecutiveWrites {
// filename
"data15.ini",
// original data
{
"[ Food ]",
"Cheese = 32",
"Ice Cream = 64",
"Bananas = 128",
"",
"[ Things ]",
"Scissors = AAA",
"Wooden Box = BBB",
"Speakers = CCC"
},
// expected result
{
"[ Food ]",
"Cheese = AAA",
"Ice Cream = BBB",
"Bananas = CCC",
"soup=DDD",
"",
"[ Things ]",
"Scissors = 32",
"Wooden Box = 64",
"Speakers = 128",
"book=256"
}
};
const T_INIFileData testDataEmptyValues {
// filename
"data16.ini",
// original data
{
"[section]",
"key=value"
},
// expected result
{
"[section]",
"key="
}
};
//
// test cases
//
const lest::test mINI_tests[] = {
CASE("TEST: Basic write")
{
// do some basic modifications to an INI file
// then compare resulting file to expected data
std::string const& filename = std::get<0>(testDataBasic);
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
ini["some section"]["some key"] = "2";
ini["some section"]["yet another key"] = "3";
EXPECT(file.write(ini) == true);
EXPECT(verifyData(testDataBasic));
},
CASE("TEST: Garbage data")
{
auto const& filename = std::get<0>(testDataWithGarbage);
// read from file
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
// update data
ini["section"].set({
{"a", "2"},
{"b", "3"},
{"c", "4"},
{"d", "5"},
{"e", "6"},
{"f", "7"},
{"g", "8"}
});
ini["other section"].remove("dinosaurs"); // sorry, dinosaurs
ini["extra section"].set({
{"aaa", "2"},
{"bbb", "3"},
{"ccc", "4"},
{"ddd", "5"}
});
ini["new section"]["test"] = "something";
// write to file
EXPECT(file.write(ini) == true);
// verify data
EXPECT(verifyData(testDataWithGarbage));
},
CASE("Test: Remove entry")
{
auto const& filename = std::get<0>(testDataRemEntry);
// read from file
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
// update data
ini["section"].remove("data1");
// write to file
EXPECT(file.write(ini) == true);
// verify data
EXPECT(verifyData(testDataRemEntry));
},
CASE("Test: Remove section")
{
auto const& filename = std::get<0>(testDataRemSection);
// read from file
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
// update data
ini.remove("section");
// write to file
EXPECT(file.write(ini) == true);
// verify data
EXPECT(verifyData(testDataRemSection));
},
CASE("Test: Duplicate keys")
{
auto const& filename = std::get<0>(testDataDuplicateKeys);
// read from file
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
// update data
ini["section"]["data"] = "D";
// write to file
EXPECT(file.write(ini) == true);
// verify data
EXPECT(verifyData(testDataDuplicateKeys));
},
CASE("Test: Duplicate sections")
{
auto const& filename = std::get<0>(testDataDuplicateSections);
// read from file
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
// update data
ini["section"]["data"] = "A";
// write to file
EXPECT(file.write(ini) == true);
// verify data
EXPECT(verifyData(testDataDuplicateSections));
},
CASE("Test: Pretty print")
{
auto const& filename = std::get<0>(testDataPrettyPrint);
// read from file
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
// update data
ini["section1"]["value3"] = "C";
ini["section2"]["value2"] = "B";
ini["section3"].set({
{"value1", "A"},
{"value2", "B"}
});
// write to file
EXPECT(file.write(ini, true) == true);
// verify data
EXPECT(verifyData(testDataPrettyPrint));
},
CASE("Test: Write to empty file")
{
auto const& filename = std::get<0>(testDataEmptyFile);
// read from file
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
// update data
ini["section"]["key"] = "value";
// write to file
EXPECT(file.write(ini) == true);
// verify data
EXPECT(verifyData(testDataEmptyFile));
},
CASE("Test: Write to empty section")
{
auto const& filename = std::get<0>(testDataEmptySection);
// read from file
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
// update data
ini["section"]["key"] = "value";
// write to file
EXPECT(file.write(ini) == true);
// verify data
EXPECT(verifyData(testDataEmptySection));
},
CASE("Test: Write to a single empty section among many")
{
auto const& filename = std::get<0>(testDataManyEmptySections);
// read from file
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
// update data
ini["section3"]["key"] = "value";
// write to file
EXPECT(file.write(ini) == true);
// verify data
EXPECT(verifyData(testDataManyEmptySections));
},
CASE("Test: Write with empty section and key names")
{
auto const& filename = std::get<0>(testDataEmptyNames);
// read from file
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
// update data
ini[""]["key"] = "";
EXPECT(ini[""].remove("key2") == true);
ini[""][""] = "value3";
ini["section"][""] = "value2";
ini["section2"][""] = "";
// write to file
EXPECT(file.write(ini) == true);
// verify data
EXPECT(verifyData(testDataEmptyNames));
},
CASE("Test: Write malformed section names")
{
auto const& filename = std::get<0>(testDataMalformed1);
// read from file
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
// update data
ini["[name1"]["key"] = "value1";
ini["name2]"]["key"] = "value2";
ini["[name3]"]["key"] = "value3";
// write to file
EXPECT(file.write(ini) == true);
// verify data
EXPECT(verifyData(testDataMalformed1));
},
CASE("Test: Write malformed key names")
{
auto const& filename = std::get<0>(testDataMalformed2);
// read from file
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
// update data
ini["name"].set({
{"=", " == "},
{"a= =", "=bb"},
{"e=", " =f= "}
});
ini["name"].remove("c= =");
ini["other"]["="] = "=";
// write to file
EXPECT(file.write(ini) == true);
// verify data
EXPECT(verifyData(testDataMalformed2));
},
CASE("Test: Consecutive writes")
{
auto const& filename = std::get<0>(testDataConsecutiveWrites);
// read from file
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
// update data
ini["food"].set({
{"cheese", "AAA"},
{"ice cream", "BBB"},
{"bananas", "CCC"},
{"soup", "DDD"}
});
ini["things"].set({
{"scissors", "32"},
{"wooden box", "64"},
{" speakers ", " 128 "},
{" book ", " 256 "}
});
// write to file multiple times
for (unsigned int i = 0; i < 10; ++i)
{
EXPECT(file.write(ini) == true);
}
// verify data
EXPECT(verifyData(testDataConsecutiveWrites));
},
CASE("Test: Empty values")
{
auto const& filename = std::get<0>(testDataEmptyValues);
// read from file
mINI::INIFile file(filename);
mINI::INIStructure ini;
EXPECT(file.read(ini) == true);
// update data
ini["section"]["key"].clear();
// write to file
EXPECT(file.write(ini) == true);
// verify data
EXPECT(verifyData(testDataEmptyValues));
}
};
int main(int argc, char** argv)
{
// write test files
writeTestFile(testDataBasic);
writeTestFile(testDataWithGarbage);
writeTestFile(testDataRemEntry);
writeTestFile(testDataRemSection);
writeTestFile(testDataDuplicateKeys);
writeTestFile(testDataDuplicateSections);
writeTestFile(testDataPrettyPrint);
writeTestFile(testDataEmptyFile);
writeTestFile(testDataEmptySection);
writeTestFile(testDataManyEmptySections);
writeTestFile(testDataEmptyNames);
writeTestFile(testDataMalformed1);
writeTestFile(testDataMalformed2);
writeTestFile(testDataConsecutiveWrites);
writeTestFile(testDataEmptyValues);
// run tests
if (int failures = lest::run(mINI_tests, argc, argv))
{
return failures;
}
return std::cout << std::endl << "All tests passed!" << std::endl, EXIT_SUCCESS;
}