From 3fea945de7264e7a6ae058211203a7f2b1a42b9d Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 Jun 2020 00:18:26 +1000 Subject: First stage of updating the project to have full test harness and incorporate some of the requested changes --- tests/.gitignore | 1 + tests/example.ini | 2 + tests/old/test.cmd | 24 ++++ tests/old/test1-expected.ini | 85 ++++++++++++ tests/old/test1-input.ini | 76 +++++++++++ tests/old/test1.cpp | 166 +++++++++++++++++++++++ tests/old/testsi-EUCJP.ini | 52 +++++++ tests/old/testsi-SJIS.ini | 51 +++++++ tests/old/testsi-UTF8.ini | 50 +++++++ tests/old/testsi.cpp | 315 +++++++++++++++++++++++++++++++++++++++++++ tests/packages.config | 4 + tests/pch.cpp | 6 + tests/pch.h | 8 ++ tests/tests.ini | 13 ++ tests/tests.vcxproj | 129 ++++++++++++++++++ tests/ts-roundtrip.cpp | 163 ++++++++++++++++++++++ tests/ts-snippets.cpp | 259 +++++++++++++++++++++++++++++++++++ tests/ts-utf8.cpp | 67 +++++++++ tests/ts-wchar.cpp | 67 +++++++++ 19 files changed, 1538 insertions(+) create mode 100644 tests/.gitignore create mode 100644 tests/example.ini create mode 100644 tests/old/test.cmd create mode 100644 tests/old/test1-expected.ini create mode 100644 tests/old/test1-input.ini create mode 100644 tests/old/test1.cpp create mode 100644 tests/old/testsi-EUCJP.ini create mode 100644 tests/old/testsi-SJIS.ini create mode 100644 tests/old/testsi-UTF8.ini create mode 100644 tests/old/testsi.cpp create mode 100644 tests/packages.config create mode 100644 tests/pch.cpp create mode 100644 tests/pch.h create mode 100644 tests/tests.ini create mode 100644 tests/tests.vcxproj create mode 100644 tests/ts-roundtrip.cpp create mode 100644 tests/ts-snippets.cpp create mode 100644 tests/ts-utf8.cpp create mode 100644 tests/ts-wchar.cpp (limited to 'tests') diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..51ade18 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +example2.ini diff --git a/tests/example.ini b/tests/example.ini new file mode 100644 index 0000000..43b252c --- /dev/null +++ b/tests/example.ini @@ -0,0 +1,2 @@ +[section] +key = value diff --git a/tests/old/test.cmd b/tests/old/test.cmd new file mode 100644 index 0000000..2d319d5 --- /dev/null +++ b/tests/old/test.cmd @@ -0,0 +1,24 @@ +@echo off + +Debug\testsi.exe -u -m -l test1-input.ini > test1-blah.ini +fc test1-expected.ini test1-output.ini +if errorlevel 1 goto error + +"Debug Unicode\testsi.exe" -u -m -l test1-input.ini > test1-blah.ini +fc test1-expected.ini test1-output.ini +if errorlevel 1 goto error + +Release\testsi.exe -u -m -l test1-input.ini > test1-blah.ini +fc test1-expected.ini test1-output.ini +if errorlevel 1 goto error + +"Release Unicode\testsi.exe" -u -m -l test1-input.ini > test1-blah.ini +fc test1-expected.ini test1-output.ini +if errorlevel 1 goto error + +exit /b 0 + +:error +echo Failed during test run. Output file doesn't match expected file. +pause +exit /b 1 diff --git a/tests/old/test1-expected.ini b/tests/old/test1-expected.ini new file mode 100644 index 0000000..dde335d --- /dev/null +++ b/tests/old/test1-expected.ini @@ -0,0 +1,85 @@ +; testsi-UTF8-std.ini : standard UTF-8 test file for SimpleIni automated testing +; +; The number after a section or key is the order that it is defined in this file +; to make it easier to see if it has been written out correctly. This file should +; be loaded with Unicode / MultiKey / MultiLine turned on. + + + +; This comment should be joined on to the one below it about the key +; with no section. + +; Key with no section +lonely-key = nosection +another = nosection either + +; This key has no value +empty = + + +; This should be joined with the comment below about japanese. +; Another line which will be un-indented. + +; This is a section of keys showing the word Japanese in different syllabies. +[ordered-1] +a-1 = blah + +; this is in kanji +japanese-2 = 日本語 + +; this is in hiragana +japanese-3 = にほんご + +; this is in katakana +japanese-4 = ニホンゴ + +; this is in romaji +japanese-5 = nihongo + +; kanji as the key +日本語-6 = japanese + + +[multi-2] + +; value a +test = a + +; value b +test = b + +; value c +test = c + +; value d +test = d + + +[multiline-3] + +; This is obviously a multi-line entry +multiline-1 = << +// Source: http://code.jellycan.com/simpleini/ +// +// Automated testing for SimpleIni streams + +#ifdef _WIN32 +# pragma warning(disable: 4786) +#endif + +#ifdef _WIN32 +# include +# define DELETE_FILE DeleteFileA +#else +# include +# define DELETE_FILE unlink +#endif +#include + +#define SI_SUPPORT_IOSTREAMS +#include "SimpleIni.h" + +class Test +{ + std::string m_strTest; + +public: + Test(const char * a_pszName) + : m_strTest(a_pszName) + { + printf("%s: test starting\n", m_strTest.c_str()); + } + + bool Success() + { + printf("%s: test succeeded\n", m_strTest.c_str()); + return false; + } + + bool Failure(const char * pszReason) + { + printf("%s: test FAILED (%s)\n", m_strTest.c_str(), pszReason); + return false; + } +}; + +bool FileComparisonTest(const char * a_pszFile1, const char * a_pszFile2) { + // ensure that the two files are the same + try { + std::string strFile1, strFile2; + + char szBuf[1024]; + FILE * fp = NULL; + +#if __STDC_WANT_SECURE_LIB__ + fopen_s(&fp, a_pszFile1, "rb"); +#else + fp = fopen(a_pszFile1, "rb"); +#endif + if (!fp) throw false; + while (!feof(fp)) { + size_t n = fread(szBuf, 1, sizeof(szBuf), fp); + strFile1.append(szBuf, n); + } + fclose(fp); + + fp = NULL; +#if __STDC_WANT_SECURE_LIB__ + fopen_s(&fp, a_pszFile2, "rb"); +#else + fp = fopen(a_pszFile2, "rb"); +#endif + if (!fp) throw false; + while (!feof(fp)) { + size_t n = fread(szBuf, 1, sizeof(szBuf), fp); + strFile2.append(szBuf, n); + } + fclose(fp); + + if (strFile1 != strFile2) throw false; + } + catch (...) { + return false; + } + + return true; +} + +bool FileLoadTest(const char * a_pszFile1, const char * a_pszFile2) { + // ensure that the two files load into simpleini the same + CSimpleIniA ini(true, true, true); + bool b; + try { + ini.Reset(); + if (ini.LoadFile(a_pszFile1) < 0) throw "Load failed for file 1"; + if (ini.SaveFile("test1.ini") < 0) throw "Save failed for file 1"; + + ini.Reset(); + if (ini.LoadFile(a_pszFile2) < 0) throw "Load failed for file 2"; + if (ini.SaveFile("test2.ini") < 0) throw "Save failed for file 2"; + + b = FileComparisonTest("test1.ini", "test2.ini"); + DELETE_FILE("test1.ini"); + DELETE_FILE("test2.ini"); + if (!b) throw "File comparison failed in FileLoadTest"; + } + catch (...) { + return false; + } + + return true; +} + +bool TestStreams() +{ + const char * rgszTestFile[3] = { + "test1-input.ini", + "test1-output.ini", + "test1-expected.ini" + }; + + Test oTest("TestStreams"); + + CSimpleIniW ini; + ini.SetUnicode(true); + ini.SetMultiKey(true); + ini.SetMultiLine(true); + + // load the file + try { + std::ifstream instream; + instream.open(rgszTestFile[0], std::ifstream::in | std::ifstream::binary); + if (ini.LoadData(instream) < 0) throw false; + instream.close(); + } + catch (...) { + return oTest.Failure("Failed to load file"); + } + + // standard contents test + //if (!StandardContentsTest(ini, oTest)) { + // return false; + //} + + // save the file + try { + std::ofstream outfile; + outfile.open(rgszTestFile[1], std::ofstream::out | std::ofstream::binary); + if (ini.Save(outfile, true) < 0) throw false; + outfile.close(); + } + catch (...) { + return oTest.Failure("Failed to save file"); + } + + // file comparison test + if (!FileComparisonTest(rgszTestFile[1], rgszTestFile[2])) { + return oTest.Failure("Failed file comparison"); + } + if (!FileLoadTest(rgszTestFile[1], rgszTestFile[2])) { + return oTest.Failure("Failed file load comparison"); + } + + return oTest.Success(); +} diff --git a/tests/old/testsi-EUCJP.ini b/tests/old/testsi-EUCJP.ini new file mode 100644 index 0000000..16d1d06 --- /dev/null +++ b/tests/old/testsi-EUCJP.ini @@ -0,0 +1,52 @@ +; test file for SimpleIni + +nosection=ok +NOSECTION=still ok + whitespace = ok + +[standard] +foo=foo1 +standard-1=foo +ܸ=ok1 + +[Standard] +Foo=foo2 +standard-2=foo +ܸ=ok2 + + [ Whitespace ] + +a= + +[ whitespace in section name ] + whitespace in key name = whitespace in value name + +; comments + ; more comments + +invalid +=invalid +====invalid + +[Japanese] +nihongo = ܸ +ܸ = ܸ + +[ܸ] +nihongo = ܸ +ܸ = ܸ + +[] +more=no section name + + + +[MultiLine] +single = This is a single line. +multi = << +// Source: http://code.jellycan.com/simpleini/ +// +// Demo of usage + +#ifdef _WIN32 +# pragma warning(disable: 4786) +#endif + +#include +#include +#include + +#define SI_SUPPORT_IOSTREAMS +#if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE) +# include +#endif + +//#define SI_CONVERT_GENERIC +//#define SI_CONVERT_ICU +//#define SI_CONVERT_WIN32 +#include "../SimpleIni.h" + +#ifdef SI_CONVERT_ICU +// if converting using ICU then we need the ICU library +# pragma comment(lib, "icuuc.lib") +#endif + +#ifdef _WIN32 +# include +#else // !_WIN32 +# define TCHAR char +# define _T(x) x +# define _tprintf printf +# define _tmain main +#endif // _WIN32 + +static void +Test( + CSimpleIni & ini + ) +{ + const TCHAR *pszSection = 0; + const TCHAR *pItem = 0; + const TCHAR *pszVal = 0; + + // get the value of the key "foo" in section "standard" + bool bHasMulti; + pszVal = ini.GetValue(_T("standard"), _T("foo"), 0, &bHasMulti); + _tprintf(_T("\n-- Value of standard::foo is '%s' (hasMulti = %d)\n"), + pszVal ? pszVal : _T("(null)"), bHasMulti); + + // set the value of the key "foo" in section "standard" + ini.SetValue(_T("standard"), _T("foo"), _T("wibble")); + pszVal = ini.GetValue(_T("standard"), _T("foo"), 0, &bHasMulti); + _tprintf(_T("\n-- Value of standard::foo is '%s' (hasMulti = %d)\n"), + pszVal ? pszVal : _T("(null)"), bHasMulti); + + // get all values of the key "foo" in section "standard" + CSimpleIni::TNamesDepend values; + if (ini.GetAllValues(_T("standard"), _T("foo"), values)) { + _tprintf(_T("\n-- Values of standard::foo are:\n")); + CSimpleIni::TNamesDepend::const_iterator i = values.begin(); + for (; i != values.end(); ++i) { + pszVal = i->pItem; + _tprintf(_T(" -> '%s'\n"), pszVal); + } + } + + // get the size of the section [standard] + _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"), + ini.GetSectionSize(_T("standard"))); + + // delete the key "foo" in section "standard", if it has value "bar" + ini.DeleteValue(_T("standard"), _T("foo"), _T("bar")); + pszVal = ini.GetValue(_T("standard"), _T("foo"), 0); + _tprintf(_T("\n-- Value of standard::foo is now '%s'\n"), + pszVal ? pszVal : _T("(null)")); + + // delete the key "foo" in section "standard" + ini.Delete(_T("standard"), _T("foo")); + pszVal = ini.GetValue(_T("standard"), _T("foo"), 0); + _tprintf(_T("\n-- Value of standard::foo is now '%s'\n"), + pszVal ? pszVal : _T("(null)")); + + // get the size of the section [standard] + _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"), + ini.GetSectionSize(_T("standard"))); + + // get the list of all key names for the section "standard" + _tprintf(_T("\n-- Dumping keys of section: [standard]\n")); + CSimpleIni::TNamesDepend keys; + ini.GetAllKeys(_T("standard"), keys); + + // dump all of the key names + CSimpleIni::TNamesDepend::const_iterator iKey = keys.begin(); + for ( ; iKey != keys.end(); ++iKey ) { + pItem = iKey->pItem; + _tprintf(_T("Key: %s\n"), pItem); + } + + // add a decimal value + ini.SetLongValue(_T("integer"), _T("dec"), 42, NULL, false); + ini.SetLongValue(_T("integer"), _T("hex"), 42, NULL, true); + + // add some bool values + ini.SetBoolValue(_T("bool"), _T("t"), true); + ini.SetBoolValue(_T("bool"), _T("f"), false); + + // get the values back + assert(42 == ini.GetLongValue(_T("integer"), _T("dec"))); + assert(42 == ini.GetLongValue(_T("integer"), _T("hex"))); + assert(true == ini.GetBoolValue(_T("bool"), _T("t"))); + assert(false == ini.GetBoolValue(_T("bool"), _T("f"))); + + // delete the section "standard" + ini.Delete(_T("standard"), NULL); + _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"), + ini.GetSectionSize(_T("standard"))); + + // iterate through every section in the file + _tprintf(_T("\n-- Dumping all sections\n")); + CSimpleIni::TNamesDepend sections; + ini.GetAllSections(sections); + CSimpleIni::TNamesDepend::const_iterator iSection = sections.begin(); + for ( ; iSection != sections.end(); ++iSection ) { + pszSection = iSection->pItem; + + // print the section name + printf("\n"); + if (*pszSection) { + _tprintf(_T("[%s]\n"), pszSection); + } + + // if there are keys and values... + const CSimpleIni::TKeyVal * pSectionData = ini.GetSection(pszSection); + if (pSectionData) { + // iterate over all keys and dump the key name and value + CSimpleIni::TKeyVal::const_iterator iKeyVal = pSectionData->begin(); + for ( ;iKeyVal != pSectionData->end(); ++iKeyVal) { + pItem = iKeyVal->first.pItem; + pszVal = iKeyVal->second; + _tprintf(_T("%s=%s\n"), pItem, pszVal); + } + } + } +} + +#if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE) +static bool +TestStreams( + const TCHAR * a_pszFile, + bool a_bIsUtf8, + bool a_bUseMultiKey, + bool a_bUseMultiLine + ) +{ + // load the file + CSimpleIni ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine); + _tprintf(_T("Loading file: %s\n"), a_pszFile); + std::ifstream instream; + instream.open(a_pszFile, std::ifstream::in | std::ifstream::binary); + SI_Error rc = ini.LoadData(instream); + instream.close(); + if (rc < 0) { + printf("Failed to open file.\n"); + return false; + } + + Test(ini); + + // save the file (simple) + _tprintf(_T("\n-- Saving file to: testsi-out-streams.ini\n")); + std::ofstream outstream; + outstream.open("testsi-out-streams.ini", std::ofstream::out | std::ofstream::binary); + ini.Save(outstream); + outstream.close(); + + return true; +} +#endif // SI_SUPPORT_IOSTREAMS + +static bool +TestFile( + const TCHAR * a_pszFile, + bool a_bIsUtf8, + bool a_bUseMultiKey, + bool a_bUseMultiLine + ) +{ + // load the file + CSimpleIni ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine); + _tprintf(_T("Loading file: %s\n"), a_pszFile); + SI_Error rc = ini.LoadFile(a_pszFile); + if (rc < 0) { + printf("Failed to open file.\n"); + return false; + } + + // run the tests + Test(ini); + + // save the file (simple) + _tprintf(_T("\n-- Saving file to: testsi-out.ini\n")); + ini.SaveFile("testsi-out.ini"); + + // save the file (with comments) + // Note: to save the file and add a comment to the beginning, use + // code such as the following. + _tprintf(_T("\n-- Saving file to: testsi-out-comment.ini\n")); + FILE * fp = NULL; +#if __STDC_WANT_SECURE_LIB__ + fopen_s(&fp, "testsi-out-comment.ini", "wb"); +#else + fp = fopen("testsi-out-comment.ini", "wb"); +#endif + if (fp) { + CSimpleIni::FileWriter writer(fp); + if (a_bIsUtf8) { + writer.Write(SI_UTF8_SIGNATURE); + } + + // add a string to the file in the correct text format + CSimpleIni::Converter convert = ini.GetConverter(); + convert.ConvertToStore(_T("; output from testsi.cpp test program") + SI_NEWLINE SI_NEWLINE); + writer.Write(convert.Data()); + + ini.Save(writer, false); + fclose(fp); + } + + return true; +} + +static bool +ParseCommandLine( + int argc, + TCHAR * argv[], + const TCHAR * & a_pszFile, + bool & a_bIsUtf8, + bool & a_bUseMultiKey, + bool & a_bUseMultiLine + ) +{ + a_pszFile = 0; + a_bIsUtf8 = false; + a_bUseMultiKey = false; + a_bUseMultiLine = false; + for (--argc; argc > 0; --argc) { + if (argv[argc][0] == '-') { + switch (argv[argc][1]) { + case TCHAR('u'): + a_bIsUtf8 = true; + break; + case TCHAR('m'): + a_bUseMultiKey = true; + break; + case TCHAR('l'): + a_bUseMultiLine = true; + break; + } + } + else { + a_pszFile = argv[argc]; + } + } + if (!a_pszFile) { + _tprintf( + _T("Usage: testsi [-u] [-m] [-l] iniFile\n") + _T(" -u Load file as UTF-8 (Default is to use system locale)\n") + _T(" -m Enable multiple keys\n") + _T(" -l Enable multiple line values\n") + ); + return false; + } + + return true; +} + +extern bool TestStreams(); + +int +_tmain( + int argc, + TCHAR * argv[] + ) +{ + setlocale(LC_ALL, ""); + + // start of automated testing... + TestStreams(); + + // parse the command line + const TCHAR * pszFile; + bool bIsUtf8, bUseMultiKey, bUseMultiLine; + if (!ParseCommandLine(argc, argv, pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) { + return 1; + } + + // run the test + if (!TestFile(pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) { + return 1; + } +#if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE) + if (!TestStreams(pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) { + return 1; + } +#endif + + return 0; +} + diff --git a/tests/packages.config b/tests/packages.config new file mode 100644 index 0000000..6c6422e --- /dev/null +++ b/tests/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/tests/pch.cpp b/tests/pch.cpp new file mode 100644 index 0000000..97b544e --- /dev/null +++ b/tests/pch.cpp @@ -0,0 +1,6 @@ +// +// pch.cpp +// Include the standard header and generate the precompiled header. +// + +#include "pch.h" diff --git a/tests/pch.h b/tests/pch.h new file mode 100644 index 0000000..29c81ff --- /dev/null +++ b/tests/pch.h @@ -0,0 +1,8 @@ +// +// pch.h +// Header for standard system include files. +// + +#pragma once + +#include "gtest/gtest.h" diff --git a/tests/tests.ini b/tests/tests.ini new file mode 100644 index 0000000..ea66f7a --- /dev/null +++ b/tests/tests.ini @@ -0,0 +1,13 @@ +[section1] +key1=value1 + +[section2] +test2=テスト2 +テスト=test +テスト2=テスト二 + +[検査] +key2=value2 +test2=テスト2 +テスト=test +テスト2=テスト二 diff --git a/tests/tests.vcxproj b/tests/tests.vcxproj new file mode 100644 index 0000000..5fe1d76 --- /dev/null +++ b/tests/tests.vcxproj @@ -0,0 +1,129 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {8f30a5dc-b942-4c9a-ba75-91c906ff85fa} + Win32Proj + 10.0.18362.0 + Application + v142 + Unicode + tests + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + Use + pch.h + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + Level3 + + + true + Console + + + + + Use + pch.h + Disabled + X64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + Level3 + + + true + Console + + + + + Use + pch.h + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + + + true + Console + true + true + + + + + Use + pch.h + X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + + + true + Console + true + true + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/tests/ts-roundtrip.cpp b/tests/ts-roundtrip.cpp new file mode 100644 index 0000000..3808a08 --- /dev/null +++ b/tests/ts-roundtrip.cpp @@ -0,0 +1,163 @@ +#include "pch.h" +#include +#include "../SimpleIni.h" + +class TestRoundTrip : public ::testing::Test { +protected: + void TestRoundTrip::SetUp() override; + void TestMulti(); + void TestBOM(bool useBOM); + +protected: + CSimpleIniA ini; + std::string input; + std::string output; +}; + +void TestRoundTrip::SetUp() { + ini.SetUnicode(); +} + +TEST_F(TestRoundTrip, TestStandard) { + input = + "; File comment\n" + "\n" + "\n" + "; Section 1 comment\n" + "[section1]\n" + "\n" + "\n" + "; Section 2 comment\n" + "[section2]\n" + "key1 = string\n" + "key2 = true\n" + "key3 = 3.1415\n" + ; + + SI_Error rc = ini.LoadData(input); + ASSERT_EQ(rc, SI_OK); + + const char* result = ini.GetValue("section2", "key1"); + ASSERT_STREQ(result, "string"); + + rc = ini.Save(output); + ASSERT_EQ(rc, SI_OK); + + output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); + ASSERT_STREQ(input.c_str(), output.c_str()); +} + +void TestRoundTrip::TestMulti() { + input = + "[section]\n" + "key = string1\n" + "key = string2\n" + ; + + SI_Error rc = ini.LoadData(input); + ASSERT_EQ(rc, SI_OK); + + rc = ini.Save(output); + ASSERT_EQ(rc, SI_OK); + + output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); +} + +TEST_F(TestRoundTrip, TestMultiGood) { + ini.SetMultiKey(true); + TestMulti(); + ASSERT_STREQ(input.c_str(), output.c_str()); +} + +TEST_F(TestRoundTrip, TestMultiBad) { + std::string expected = + "[section]\n" + "key = string2\n"; + + ini.SetMultiKey(false); + TestMulti(); + ASSERT_STRNE(input.c_str(), output.c_str()); + ASSERT_STREQ(expected.c_str(), output.c_str()); +} + +TEST_F(TestRoundTrip, TestSpacesTrue) { + input = + "[section]\n" + "key = string1\n"; + + SI_Error rc = ini.LoadData(input); + ASSERT_EQ(rc, SI_OK); + + ini.SetSpaces(true); + rc = ini.Save(output); + ASSERT_EQ(rc, SI_OK); + + output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); + + ASSERT_STREQ(input.c_str(), output.c_str()); +} + +TEST_F(TestRoundTrip, TestSpacesFalse) { + input = + "[section]\n" + "key = string1\n"; + + SI_Error rc = ini.LoadData(input); + ASSERT_EQ(rc, SI_OK); + + ini.SetSpaces(false); + rc = ini.Save(output); + ASSERT_EQ(rc, SI_OK); + + output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); + + ASSERT_STRNE(input.c_str(), output.c_str()); + + std::string expected = + "[section]\n" + "key=string1\n"; + + ASSERT_STREQ(expected.c_str(), output.c_str()); +} + +void TestRoundTrip::TestBOM(bool useBOM) { + const char bom[] = "\xEF\xBB\xBF"; + const char input8[] = + u8"[テスト1]\n" + u8"テスト2 = テスト3\n"; + + input = bom; + input += input8; + + ini.Reset(); + ini.SetUnicode(false); + SI_Error rc = ini.LoadData(input); + ASSERT_EQ(rc, SI_OK); + + const char tesuto1[] = u8"テスト1"; + const char tesuto2[] = u8"テスト2"; + const char tesuto3[] = u8"テスト3"; + + const char* result = ini.GetValue(tesuto1, tesuto2); + ASSERT_STREQ(result, tesuto3); + + rc = ini.Save(output, useBOM); + ASSERT_EQ(rc, SI_OK); + + output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); +} + +TEST_F(TestRoundTrip, TestWithBOM) { + TestBOM(true); + + ASSERT_STREQ(input.c_str(), output.c_str()); +} + +TEST_F(TestRoundTrip, TestWithoutBOM) { + TestBOM(false); + + ASSERT_STRNE(input.c_str(), output.c_str()); + + std::string expected(input, 3); + ASSERT_STREQ(expected.c_str(), output.c_str()); +} diff --git a/tests/ts-snippets.cpp b/tests/ts-snippets.cpp new file mode 100644 index 0000000..d18150e --- /dev/null +++ b/tests/ts-snippets.cpp @@ -0,0 +1,259 @@ +#include "pch.h" +#include "../SimpleIni.h" + + +// ### SIMPLE USAGE + +TEST(TestSnippets, TestSimple) { + // simple demonstration + + CSimpleIniA ini; + ini.SetUnicode(); + + SI_Error rc = ini.LoadFile("example.ini"); + if (rc < 0) { /* handle error */ }; + ASSERT_EQ(rc, SI_OK); + + const char* pv; + pv = ini.GetValue("section", "key", "default"); + ASSERT_STREQ(pv, "value"); + + ini.SetValue("section", "key", "newvalue"); + + pv = ini.GetValue("section", "key", "default"); + ASSERT_STREQ(pv, "newvalue"); +} + + +// ### LOADING DATA + +TEST(TestSnippets, TestLoadFile) { + // load from a data file + CSimpleIniA ini; + SI_Error rc = ini.LoadFile("example.ini"); + if (rc < 0) { /* handle error */ }; + ASSERT_EQ(rc, SI_OK); +} + +TEST(TestSnippets, TestLoadString) { + // load from a string + const std::string example = "[section]\nkey = value\n"; + CSimpleIniA ini; + SI_Error rc = ini.LoadData(example); + if (rc < 0) { /* handle error */ }; + ASSERT_EQ(rc, SI_OK); +} + + +// ### GETTING SECTIONS AND KEYS + +TEST(TestSnippets, TestSectionsAndKeys) { + const std::string example = + "[section1]\n" + "key1 = value1\n" + "key2 = value2\n" + "\n" + "[section2]\n" + "[section3]\n"; + + CSimpleIniA ini; + SI_Error rc = ini.LoadData(example); + ASSERT_EQ(rc, SI_OK); + + + + // get all sections + CSimpleIniA::TNamesDepend sections; + ini.GetAllSections(sections); + + // get all keys in a section + CSimpleIniA::TNamesDepend keys; + ini.GetAllKeys("section1", keys); + + + + const char* expectedSections[] = { "section1", "section2", "section3", nullptr }; + const char* expectedKeys[] = { "key1", "key2", nullptr }; + + CSimpleIniA::TNamesDepend::const_iterator it; + int i; + + for (i = 0, it = sections.begin(); it != sections.end(); ++i, ++it) { + ASSERT_NE(expectedSections[i], nullptr); + ASSERT_STREQ(expectedSections[i], it->pItem); + } + ASSERT_EQ(expectedSections[i], nullptr); + + for (i = 0, it = keys.begin(); it != keys.end(); ++i, ++it) { + ASSERT_NE(expectedKeys[i], nullptr); + ASSERT_STREQ(expectedKeys[i], it->pItem); + } + ASSERT_EQ(expectedKeys[i], nullptr); +} + + +// ### GETTING VALUES + +TEST(TestSnippets, TestGettingValues) { + const std::string example = + "[section1]\n" + "key1 = value1\n" + "key2 = value2.1\n" + "key2 = value2.2\n" + "\n" + "[section2]\n" + "[section3]\n"; + + bool utf8 = true; + bool multiKey = true; + CSimpleIniA ini(utf8, multiKey); + SI_Error rc = ini.LoadData(example); + ASSERT_EQ(rc, SI_OK); + + + // get the value of a key that doesn't exist + const char* pv; + pv = ini.GetValue("section1", "key99"); + ASSERT_EQ(pv, nullptr); + + // get the value of a key that does exist + pv = ini.GetValue("section1", "key1"); + ASSERT_STREQ(pv, "value1"); + + // get the value of a key which may have multiple + // values. If hasMultiple is true, then there are + // multiple values and just one value has been returned + bool hasMulti; + pv = ini.GetValue("section1", "key1", nullptr, &hasMulti); + ASSERT_STREQ(pv, "value1"); + ASSERT_EQ(hasMulti, false); + + pv = ini.GetValue("section1", "key2", nullptr, &hasMulti); + ASSERT_STREQ(pv, "value2.1"); + ASSERT_EQ(hasMulti, true); + + // get all values of a key with multiple values + CSimpleIniA::TNamesDepend values; + ini.GetAllValues("section1", "key2", values); + + // sort the values into a known order, in this case we want + // the original load order + values.sort(CSimpleIniA::Entry::LoadOrder()); + + // output all of the items + CSimpleIniA::TNamesDepend::const_iterator it; + for (it = values.begin(); it != values.end(); ++it) { + printf("value = '%s'\n", it->pItem); + } + + + int i; + const char* expectedValues[] = { "value2.1", "value2.2", nullptr }; + for (i = 0, it = values.begin(); it != values.end(); ++it, ++i) { + ASSERT_NE(expectedValues[i], nullptr); + ASSERT_STREQ(expectedValues[i], it->pItem); + } + ASSERT_EQ(expectedValues[i], nullptr); +} + + +// ### MODIFYING DATA + +TEST(TestSnippets, TestModifyingData) { + bool utf8 = true; + bool multiKey = false; + CSimpleIniA ini(utf8, multiKey); + SI_Error rc; + + + // add a new section + rc = ini.SetValue("section1", nullptr, nullptr); + if (rc < 0) { /* handle error */ }; + ASSERT_EQ(rc, SI_INSERTED); + + // not an error to add one that already exists + rc = ini.SetValue("section1", nullptr, nullptr); + if (rc < 0) { /* handle error */ }; + ASSERT_EQ(rc, SI_UPDATED); + + // get the value of a key that doesn't exist + const char* pv; + pv = ini.GetValue("section2", "key1", "default-value"); + ASSERT_STREQ(pv, "default-value"); + + // adding a key (the section will be added if needed) + rc = ini.SetValue("section2", "key1", "value1"); + if (rc < 0) { /* handle error */ }; + ASSERT_EQ(rc, SI_INSERTED); + + // ensure it is set to expected value + pv = ini.GetValue("section2", "key1", nullptr); + ASSERT_STREQ(pv, "value1"); + + // change the value of a key + rc = ini.SetValue("section2", "key1", "value2"); + if (rc < 0) { /* handle error */ }; + ASSERT_EQ(rc, SI_UPDATED); + + // ensure it is set to expected value + pv = ini.GetValue("section2", "key1", nullptr); + ASSERT_STREQ(pv, "value2"); +} + + +// ### DELETING DATA + +TEST(TestSnippets, TestDeletingData) { + const std::string example = + "[section1]\n" + "key1 = value1\n" + "key2 = value2\n" + "\n" + "[section2]\n" + "key1 = value1\n" + "key2 = value2\n" + "\n" + "[section3]\n"; + + bool utf8 = true; + CSimpleIniA ini(utf8); + SI_Error rc = ini.LoadData(example); + ASSERT_EQ(rc, SI_OK); + + + // deleting a key from a section. Optionally the entire + // section may be deleted if it is now empty. + bool done, deleteSectionIfEmpty = true; + done = ini.Delete("section1", "key1", deleteSectionIfEmpty); + ASSERT_EQ(done, true); + done = ini.Delete("section1", "key1"); + ASSERT_EQ(done, false); + + // deleting an entire section and all keys in it + done = ini.Delete("section2", nullptr); + ASSERT_EQ(done, true); + done = ini.Delete("section2", nullptr); + ASSERT_EQ(done, false); +} + + +// ### SAVING DATA + +TEST(TestSnippets, TestSavingData) { + bool utf8 = true; + CSimpleIniA ini(utf8); + SI_Error rc; + + + // save the data to a string + std::string data; + rc = ini.Save(data); + if (rc < 0) { /* handle error */ }; + ASSERT_EQ(rc, SI_OK); + + // save the data back to the file + rc = ini.SaveFile("example2.ini"); + if (rc < 0) { /* handle error */ }; + ASSERT_EQ(rc, SI_OK); +} + diff --git a/tests/ts-utf8.cpp b/tests/ts-utf8.cpp new file mode 100644 index 0000000..9208648 --- /dev/null +++ b/tests/ts-utf8.cpp @@ -0,0 +1,67 @@ +#include "pch.h" +#include "../SimpleIni.h" + +class TestUTF8 : public ::testing::Test { +protected: + void TestUTF8::SetUp() override; +protected: + CSimpleIniA ini; +}; + +void TestUTF8::SetUp() { + ini.SetUnicode(); + SI_Error err = ini.LoadFile("tests.ini"); + ASSERT_EQ(err, SI_OK); +} + +TEST_F(TestUTF8, TestSectionAKeyAValA) { + const char* result = ini.GetValue("section1", "key1"); + ASSERT_STREQ(result, "value1"); +} + +TEST_F(TestUTF8, TestSectionAKeyAValU) { + const char tesuto2[] = u8"テスト2"; + const char* result = ini.GetValue("section2", "test2"); + ASSERT_STREQ(result, tesuto2); +} + +TEST_F(TestUTF8, TestSectionAKeyUValA) { + const char tesuto[] = u8"テスト"; + const char* result = ini.GetValue("section2", tesuto); + ASSERT_STREQ(result, "test"); +} + +TEST_F(TestUTF8, TestSectionAKeyUValU) { + const char tesuto2[] = u8"テスト2"; + const char tesutoni[] = u8"テスト二"; + const char* result = ini.GetValue("section2", tesuto2); + ASSERT_STREQ(result, tesutoni); +} + +TEST_F(TestUTF8, TestSectionUKeyAValA) { + const char kensa[] = u8"検査"; + const char* result = ini.GetValue(kensa, "key2"); + ASSERT_STREQ(result, "value2"); +} + +TEST_F(TestUTF8, TestSectionUKeyAValU) { + const char kensa[] = u8"検査"; + const char tesuto2[] = u8"テスト2"; + const char* result = ini.GetValue(kensa, "test2"); + ASSERT_STREQ(result, tesuto2); +} + +TEST_F(TestUTF8, TestSectionUKeyUValA) { + const char kensa[] = u8"検査"; + const char tesuto[] = u8"テスト"; + const char* result = ini.GetValue(kensa, tesuto); + ASSERT_STREQ(result, "test"); +} + +TEST_F(TestUTF8, TestSectionUKeyUValU) { + const char kensa[] = u8"検査"; + const char tesuto2[] = u8"テスト2"; + const char tesutoni[] = u8"テスト二"; + const char* result = ini.GetValue(kensa, tesuto2); + ASSERT_STREQ(result, tesutoni); +} diff --git a/tests/ts-wchar.cpp b/tests/ts-wchar.cpp new file mode 100644 index 0000000..8041272 --- /dev/null +++ b/tests/ts-wchar.cpp @@ -0,0 +1,67 @@ +#include "pch.h" +#include "../SimpleIni.h" + +class TestWide : public ::testing::Test { +protected: + void TestWide::SetUp() override; +protected: + CSimpleIniW ini; +}; + +void TestWide::SetUp() { + ini.SetUnicode(); + SI_Error err = ini.LoadFile(L"tests.ini"); + ASSERT_EQ(err, SI_OK); +} + +TEST_F(TestWide, TestSectionAKeyAValA) { + const wchar_t* result = ini.GetValue(L"section1", L"key1"); + ASSERT_STREQ(result, L"value1"); +} + +TEST_F(TestWide, TestSectionAKeyAValU) { + const wchar_t tesuto2[] = L"テスト2"; + const wchar_t* result = ini.GetValue(L"section2", L"test2"); + ASSERT_STREQ(result, tesuto2); +} + +TEST_F(TestWide, TestSectionAKeyUValA) { + const wchar_t tesuto[] = L"テスト"; + const wchar_t* result = ini.GetValue(L"section2", tesuto); + ASSERT_STREQ(result, L"test"); +} + +TEST_F(TestWide, TestSectionAKeyUValU) { + const wchar_t tesuto2[] = L"テスト2"; + const wchar_t tesutoni[] = L"テスト二"; + const wchar_t* result = ini.GetValue(L"section2", tesuto2); + ASSERT_STREQ(result, tesutoni); +} + +TEST_F(TestWide, TestSectionUKeyAValA) { + const wchar_t kensa[] = L"検査"; + const wchar_t* result = ini.GetValue(kensa, L"key2"); + ASSERT_STREQ(result, L"value2"); +} + +TEST_F(TestWide, TestSectionUKeyAValU) { + const wchar_t kensa[] = L"検査"; + const wchar_t tesuto2[] = L"テスト2"; + const wchar_t* result = ini.GetValue(kensa, L"test2"); + ASSERT_STREQ(result, tesuto2); +} + +TEST_F(TestWide, TestSectionUKeyUValA) { + const wchar_t kensa[] = L"検査"; + const wchar_t tesuto[] = L"テスト"; + const wchar_t* result = ini.GetValue(kensa, tesuto); + ASSERT_STREQ(result, L"test"); +} + +TEST_F(TestWide, TestSectionUKeyUValU) { + const wchar_t kensa[] = L"検査"; + const wchar_t tesuto2[] = L"テスト2"; + const wchar_t tesutoni[] = L"テスト二"; + const wchar_t* result = ini.GetValue(kensa, tesuto2); + ASSERT_STREQ(result, tesutoni); +} -- cgit v1.2.3