From fce4a5270bb057a44e0b1800c34f14b7a175a83c Mon Sep 17 00:00:00 2001 From: SinghRajenM Date: Sun, 8 Sep 2024 16:45:55 +0530 Subject: [PATCH 1/3] Added googletest submodule --- .gitmodules | 3 +++ external/googletest | 1 + 2 files changed, 4 insertions(+) create mode 160000 external/googletest diff --git a/.gitmodules b/.gitmodules index 9b9cf14..bda5b04 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "external/rapidjson"] path = external/rapidjson url = https://github.com/NPP-JSONViewer/rapidjson.git +[submodule "external/googletest"] + path = external/googletest + url = https://github.com/google/googletest.git diff --git a/external/googletest b/external/googletest new file mode 160000 index 0000000..0953a17 --- /dev/null +++ b/external/googletest @@ -0,0 +1 @@ +Subproject commit 0953a17a4281fc26831da647ad3fcd5e21e6473b From 2b614082ef5c81033ca2d92ffd1d0d04501adba8 Mon Sep 17 00:00:00 2001 From: SinghRajenM Date: Sun, 8 Sep 2024 16:46:33 +0530 Subject: [PATCH 2/3] Updated common propertysheet --- src/NppJsonViewer/NPPJSONViewer.vcxproj | 18 ------------------ src/NppJsonViewerCommon.props | 1 + 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/NppJsonViewer/NPPJSONViewer.vcxproj b/src/NppJsonViewer/NPPJSONViewer.vcxproj index 95079a7..a2fa251 100644 --- a/src/NppJsonViewer/NPPJSONViewer.vcxproj +++ b/src/NppJsonViewer/NPPJSONViewer.vcxproj @@ -106,24 +106,6 @@ false false - - $(SolutionDir)UtilityLib;$(IncludePath) - - - $(SolutionDir)UtilityLib;$(IncludePath) - - - $(SolutionDir)UtilityLib;$(IncludePath) - - - $(SolutionDir)UtilityLib;$(IncludePath) - - - $(SolutionDir)UtilityLib;$(IncludePath) - - - $(SolutionDir)UtilityLib;$(IncludePath) - Disabled diff --git a/src/NppJsonViewerCommon.props b/src/NppJsonViewerCommon.props index b30397c..85f3368 100644 --- a/src/NppJsonViewerCommon.props +++ b/src/NppJsonViewerCommon.props @@ -6,6 +6,7 @@ $(SolutionDir)Build\Bin\$(Configuration)\$(Platform)\ $(SolutionDir)Build\Intermediate\$(ProjectName)\$(Configuration)\$(Platform)\ $(SolutionDir)..\external\npp;$(SolutionDir)..\external\rapidjson\include;$(ExternalIncludePath) + $(SolutionDir)UtilityLib;$(IncludePath) From d7c06d8f2f40640a3866e3e510184e17cd0f78cb Mon Sep 17 00:00:00 2001 From: SinghRajenM Date: Sun, 8 Sep 2024 16:47:39 +0530 Subject: [PATCH 3/3] Added unit test project --- .github/workflows/ci_build.yml | 21 ++- src/NPPJSONViewer.sln | 14 ++ src/UtilityLib/Utility.cpp | 2 +- tests/UnitTest/StringHelperTest.cpp | 166 ++++++++++++++++++++++++ tests/UnitTest/UnitTest.vcxproj | 161 +++++++++++++++++++++++ tests/UnitTest/UnitTest.vcxproj.filters | 37 ++++++ tests/UnitTest/UtilityTest.cpp | 140 ++++++++++++++++++++ tests/UnitTest/main.cpp | 9 ++ 8 files changed, 547 insertions(+), 3 deletions(-) create mode 100644 tests/UnitTest/StringHelperTest.cpp create mode 100644 tests/UnitTest/UnitTest.vcxproj create mode 100644 tests/UnitTest/UnitTest.vcxproj.filters create mode 100644 tests/UnitTest/UtilityTest.cpp create mode 100644 tests/UnitTest/main.cpp diff --git a/.github/workflows/ci_build.yml b/.github/workflows/ci_build.yml index db3cb6e..412b378 100644 --- a/.github/workflows/ci_build.yml +++ b/.github/workflows/ci_build.yml @@ -1,4 +1,4 @@ -name: CI Build +name: Build and test on: push: @@ -17,28 +17,45 @@ jobs: build_platform: [Win32, x64, ARM64] steps: + # Step 1: Check out the code from the repo - name: Checkout repo uses: actions/checkout@v4 with: submodules: recursive + # Step 2: Prepare for build - name: Pre Build uses: microsoft/setup-msbuild@v2 + # Step 3: Build projects and unit test - name: Build working-directory: src run: msbuild NppJSONViewer.sln /m /p:configuration="${{ matrix.build_configuration }}" /p:platform="${{ matrix.build_platform }}" /p:PlatformToolset="v143" + # Step 4: Upload build binary artifacts - name: Archive binaries artifacts uses: actions/upload-artifact@v3 with: name: ${{ matrix.build_platform}}_${{ matrix.build_configuration}} path: src\Build\Bin\${{ matrix.build_configuration}}\${{ matrix.build_platform}}\NPPJSONViewer.dll + # Step 5: Upload build pdb artifacts - name: Archive symbols artifacts uses: actions/upload-artifact@v3 with: name: ${{ matrix.build_platform}}_${{ matrix.build_configuration}}_pdb path: src\Build\Bin\${{ matrix.build_configuration}}\${{ matrix.build_platform}}\NPPJSONViewer.pdb - + # Step 6: Run unit tests for x86 | Release + - name: Run tests x86 | Release + if: matrix.build_platform == 'Win32' && matrix.build_configuration == 'Release' + run: | + cd src\Build\Bin\Release\Win32 + ./UnitTest.exe + + # Step 7: Run unit tests for x64 | Release + - name: Run tests x64 | Release + if: matrix.build_platform == 'x64' && matrix.build_configuration == 'Release' + run: | + cd src\Build\Bin\Release\x64 + ./UnitTest.exe diff --git a/src/NPPJSONViewer.sln b/src/NPPJSONViewer.sln index f72b894..69a95ef 100644 --- a/src/NPPJSONViewer.sln +++ b/src/NPPJSONViewer.sln @@ -7,6 +7,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NPPJSONViewer", "NppJsonVie EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UtilityLib", "UtilityLib\UtilityLib.vcxproj", "{171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTest", "..\tests\UnitTest\UnitTest.vcxproj", "{5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -41,6 +43,18 @@ Global {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Release|Win32.Build.0 = Release|Win32 {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Release|x64.ActiveCfg = Release|x64 {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Release|x64.Build.0 = Release|x64 + {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Debug|ARM64.Build.0 = Debug|ARM64 + {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Debug|Win32.ActiveCfg = Debug|Win32 + {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Debug|Win32.Build.0 = Debug|Win32 + {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Debug|x64.ActiveCfg = Debug|x64 + {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Debug|x64.Build.0 = Debug|x64 + {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Release|ARM64.ActiveCfg = Release|ARM64 + {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Release|ARM64.Build.0 = Release|ARM64 + {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Release|Win32.ActiveCfg = Release|Win32 + {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Release|Win32.Build.0 = Release|Win32 + {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Release|x64.ActiveCfg = Release|x64 + {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/UtilityLib/Utility.cpp b/src/UtilityLib/Utility.cpp index 040e637..6da20bc 100644 --- a/src/UtilityLib/Utility.cpp +++ b/src/UtilityLib/Utility.cpp @@ -185,7 +185,7 @@ auto CUtility::GetFileExtension(const std::wstring &fileName) -> std::wstring auto CUtility::GetTempFilePath() -> std::wstring { - TCHAR tmpDir[1024]; + TCHAR tmpDir[1024] {}; GetTempPath(1024, tmpDir); return tmpDir; } diff --git a/tests/UnitTest/StringHelperTest.cpp b/tests/UnitTest/StringHelperTest.cpp new file mode 100644 index 0000000..6dd8e01 --- /dev/null +++ b/tests/UnitTest/StringHelperTest.cpp @@ -0,0 +1,166 @@ +#include + +#include +#include + +#include "StringHelper.h" + +namespace Utility +{ + class StringHelperTest : public ::testing::Test + { + protected: + void SetUp() override {} + void TearDown() override {} + }; + + // Test ReplaceAll with simple strings + TEST_F(StringHelperTest, ReplaceAll_ShouldReplaceSubstring) + { + std::string result = StringHelper::ReplaceAll("hello world", "world", "C++"); + EXPECT_EQ(result, "hello C++"); + } + + TEST_F(StringHelperTest, ReplaceAll_ShouldReplaceMultipleOccurrences) + { + std::string result = StringHelper::ReplaceAll("a quick brown fox jumps over the lazy dog", "o", "0"); + EXPECT_EQ(result, "a quick br0wn f0x jumps 0ver the lazy d0g"); + } + + TEST_F(StringHelperTest, ReplaceAll_NoOccurrences) + { + std::string result = StringHelper::ReplaceAll("hello world", "foo", "bar"); + EXPECT_EQ(result, "hello world"); + } + + // Test ReplaceAll for wide strings + TEST_F(StringHelperTest, ReplaceAll_WideString_ShouldReplaceSubstring) + { + std::wstring result = StringHelper::ReplaceAll(L"hello world", L"world", L"C++"); + EXPECT_EQ(result, L"hello C++"); + } + + TEST_F(StringHelperTest, ReplaceAll_WideString_NoOccurrences) + { + std::wstring result = StringHelper::ReplaceAll(L"hello world", L"foo", L"bar"); + EXPECT_EQ(result, L"hello world"); + } + + // Test ToWstring with various encodings + TEST_F(StringHelperTest, ToWstring_ShouldConvertString) + { + std::wstring result = StringHelper::ToWstring("hello", CP_UTF8); + EXPECT_EQ(result, L"hello"); + } + + TEST_F(StringHelperTest, ToWstring_ShouldHandleEmptyString) + { + std::wstring result = StringHelper::ToWstring("", CP_UTF8); + EXPECT_EQ(result, L""); + } + + TEST_F(StringHelperTest, ToWstring_InvalidEncoding_ShouldReturnEmpty) + { + std::wstring result = StringHelper::ToWstring("invalid", 9999); // Invalid codepage + EXPECT_EQ(result, L""); + } + + // Test ToString with various encodings + TEST_F(StringHelperTest, ToString_ShouldConvertWstring) + { + std::string result = StringHelper::ToString(L"hello", CP_UTF8); + EXPECT_EQ(result, "hello"); + } + + TEST_F(StringHelperTest, ToString_ShouldHandleEmptyWstring) + { + std::string result = StringHelper::ToString(L"", CP_UTF8); + EXPECT_EQ(result, ""); + } + + TEST_F(StringHelperTest, ToString_InvalidEncoding_ShouldReturnEmpty) + { + std::string result = StringHelper::ToString(L"invalid", 9999); // Invalid codepage + EXPECT_EQ(result, ""); + } + + // Test Split for standard strings + TEST_F(StringHelperTest, Split_ShouldSplitStringByDelimiter) + { + std::vector result = StringHelper::Split("a,b,c", ","); + std::vector expected = {"a", "b", "c"}; + EXPECT_EQ(result.size(), expected.size()); + EXPECT_EQ(result, expected); + } + + TEST_F(StringHelperTest, Split_ShouldHandleEmptyString) + { + std::vector result = StringHelper::Split("", ","); + EXPECT_TRUE(result.empty()); + } + + TEST_F(StringHelperTest, Split_ShouldHandleNoDelimiters) + { + std::vector result = StringHelper::Split("abc", ","); + std::vector expected = {"abc"}; + EXPECT_EQ(result.size(), expected.size()); + EXPECT_EQ(result, expected); + } + + // Test Split for wide strings + TEST_F(StringHelperTest, Split_WideString_ShouldSplitStringByDelimiter) + { + std::vector result = StringHelper::Split(L"a,b,c", L","); + std::vector expected = {L"a", L"b", L"c"}; + EXPECT_EQ(result.size(), expected.size()); + EXPECT_EQ(result, expected); + } + + TEST_F(StringHelperTest, Split_WideString_ShouldHandleEmptyString) + { + std::vector result = StringHelper::Split(L"", L","); + EXPECT_TRUE(result.empty()); + } + + TEST_F(StringHelperTest, Split_WideString_ShouldHandleNoDelimiters) + { + std::vector result = StringHelper::Split(L"abc", L","); + std::vector expected = {L"abc"}; + EXPECT_EQ(result.size(), expected.size()); + EXPECT_EQ(result, expected); + } + + // Test Contains method with case sensitivity + TEST_F(StringHelperTest, Contains_ShouldFindSubstring) + { + bool result = StringHelper::Contains("hello world", "world", false); + EXPECT_TRUE(result); + } + + TEST_F(StringHelperTest, Contains_ShouldReturnFalseForNonExistingSubstring) + { + bool result = StringHelper::Contains("hello world", "foo", false); + EXPECT_FALSE(result); + } + + TEST_F(StringHelperTest, Contains_IgnoreCase_ShouldFindSubstring) + { + bool result = StringHelper::Contains("Hello World", "world", true); + EXPECT_TRUE(result); + } + + // Test ToLower + TEST_F(StringHelperTest, ToLower_ShouldConvertStringToLowercase) + { + std::string input = "HeLLo WoRLD"; + StringHelper::ToLower(input); + EXPECT_EQ(input, "hello world"); + } + + TEST_F(StringHelperTest, ToLower_WideString_ShouldConvertToLowercase) + { + std::wstring input = L"HeLLo WoRLD"; + StringHelper::ToLower(input); + EXPECT_EQ(input, L"hello world"); + } +} // namespace Utility diff --git a/tests/UnitTest/UnitTest.vcxproj b/tests/UnitTest/UnitTest.vcxproj new file mode 100644 index 0000000..227793c --- /dev/null +++ b/tests/UnitTest/UnitTest.vcxproj @@ -0,0 +1,161 @@ + + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {5a15fd53-e7c1-4f10-85fa-b7c3bb5d4d64} + Win32Proj + 10.0.22621.0 + Application + v143 + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)..\external\googletest\googletest;$(SolutionDir)..\external\googletest\googletest\include;$(SolutionDir)..\external\googletest\googlemock;$(SolutionDir)..\external\googletest\googlemock\include;$(ExternalIncludePath) + true + + + $(SolutionDir)..\external\googletest\googletest;$(SolutionDir)..\external\googletest\googletest\include;$(SolutionDir)..\external\googletest\googlemock;$(SolutionDir)..\external\googletest\googlemock\include;$(ExternalIncludePath) + + + $(SolutionDir)..\external\googletest\googletest;$(SolutionDir)..\external\googletest\googletest\include;$(SolutionDir)..\external\googletest\googlemock;$(SolutionDir)..\external\googletest\googlemock\include;$(ExternalIncludePath) + true + + + $(SolutionDir)..\external\googletest\googletest;$(SolutionDir)..\external\googletest\googletest\include;$(SolutionDir)..\external\googletest\googlemock;$(SolutionDir)..\external\googletest\googlemock\include;$(ExternalIncludePath) + + + $(SolutionDir)..\external\googletest\googletest;$(SolutionDir)..\external\googletest\googletest\include;$(SolutionDir)..\external\googletest\googlemock;$(SolutionDir)..\external\googletest\googlemock\include;$(ExternalIncludePath) + + + $(SolutionDir)..\external\googletest\googletest;$(SolutionDir)..\external\googletest\googletest\include;$(SolutionDir)..\external\googletest\googlemock;$(SolutionDir)..\external\googletest\googlemock\include;$(ExternalIncludePath) + + + + + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + EditAndContinue + + + true + Console + false + + + + + Disabled + X64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + + + true + Console + + + + + Disabled + X64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + + + true + Console + + + + + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ProgramDatabase + + + true + Console + + + + + X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ProgramDatabase + + + true + Console + + + + + X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ProgramDatabase + + + true + Console + + + + + + + + + + + + {171cafc6-e679-4b81-bf5b-049ac0fab4f8} + + + + + + \ No newline at end of file diff --git a/tests/UnitTest/UnitTest.vcxproj.filters b/tests/UnitTest/UnitTest.vcxproj.filters new file mode 100644 index 0000000..bc4ffe8 --- /dev/null +++ b/tests/UnitTest/UnitTest.vcxproj.filters @@ -0,0 +1,37 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {6566c3ee-c762-478e-89d9-b411a9670e1a} + + + + + Source Files + + + Source Files\googletest + + + Source Files\googletest + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/tests/UnitTest/UtilityTest.cpp b/tests/UnitTest/UtilityTest.cpp new file mode 100644 index 0000000..8b89f1d --- /dev/null +++ b/tests/UnitTest/UtilityTest.cpp @@ -0,0 +1,140 @@ +#include + +#include +#include +#include + +#include "Utility.h" + +namespace Utility +{ + // Test GetXFromLPARAM with sample LPARAM + TEST(CUtilityTest, GetXFromLPARAM) + { + CUtility util; + LPARAM lp = MAKELPARAM(50, 100); + short x = util.GetXFromLPARAM(lp); + EXPECT_EQ(x, 50); + } + + // Test GetYFromLPARAM with sample LPARAM + TEST(CUtilityTest, GetYFromLPARAM) + { + CUtility util; + LPARAM lp = MAKELPARAM(50, 100); + short y = util.GetYFromLPARAM(lp); + EXPECT_EQ(y, 100); + } + + // Test DirExist with a valid directory + TEST(CUtilityTest, DirExist_ValidDir) + { + CUtility util; + bool exists = util.DirExist(L"C:\\Windows"); + EXPECT_TRUE(exists); + } + + // Test DirExist with an invalid directory + TEST(CUtilityTest, DirExist_InvalidDir) + { + CUtility util; + bool exists = util.DirExist(L"Z:\\InvalidDir"); + EXPECT_FALSE(exists); + } + + // Test FileExist with a valid file + TEST(CUtilityTest, FileExist_ValidFile) + { + CUtility util; + bool exists = util.FileExist(L"C:\\Windows\\notepad.exe"); + EXPECT_TRUE(exists); + } + + // Test FileExist with an invalid file + TEST(CUtilityTest, FileExist_InvalidFile) + { + CUtility util; + bool exists = util.FileExist(L"C:\\InvalidPath\\file.exe"); + EXPECT_FALSE(exists); + } + + // Test FileSize with a valid file + TEST(CUtilityTest, FileSize_ValidFile) + { + CUtility util; + long size = util.FileSize(L"C:\\Windows\\notepad.exe"); + EXPECT_GT(size, 0); + } + + // Test FileSize with an invalid file + TEST(CUtilityTest, FileSize_InvalidFile) + { + CUtility util; + EXPECT_THROW(util.FileSize(L"C:\\InvalidPath\\file.exe"), std::filesystem::filesystem_error); + } + + // Test GetFileName with extension + TEST(CUtilityTest, GetFileName_WithExtension) + { + CUtility util; + std::wstring fileName = util.GetFileName(L"C:\\path\\file.txt", true); + EXPECT_EQ(fileName, L"file.txt"); + } + + // Test GetFileName without extension + TEST(CUtilityTest, GetFileName_WithoutExtension) + { + CUtility util; + std::wstring fileName = util.GetFileName(L"C:\\path\\file.txt", false); + EXPECT_EQ(fileName, L"file"); + } + + // Test GetFileExtension with a valid file name + TEST(CUtilityTest, GetFileExtension_ValidFileName) + { + CUtility util; + std::wstring ext = util.GetFileExtension(L"file.txt"); + EXPECT_EQ(ext, L".txt"); + } + + // Test GetTempFilePath + TEST(CUtilityTest, GetTempFilePath) + { + CUtility util; + std::wstring tempPath = util.GetTempFilePath(); + EXPECT_FALSE(tempPath.empty()); + } + + // Test IsNumber with a valid number string + TEST(CUtilityTest, IsNumber_ValidNumber) + { + CUtility util; + bool isNumber = util.IsNumber(L"12345"); + EXPECT_TRUE(isNumber); + } + + // Test IsNumber with an invalid number string + TEST(CUtilityTest, IsNumber_InvalidNumber) + { + CUtility util; + bool isNumber = util.IsNumber(L"123a45"); + EXPECT_FALSE(isNumber); + } + + // Test GetNumber with valid number string + TEST(CUtilityTest, GetNumber_ValidNumber) + { + CUtility util; + auto number = util.GetNumber(L"12345"); + ASSERT_TRUE(number.has_value()); + EXPECT_EQ(number.value(), 12345); + } + + // Test GetNumber with invalid number string + TEST(CUtilityTest, GetNumber_InvalidNumber) + { + CUtility util; + auto number = util.GetNumber(L"123a45"); + EXPECT_FALSE(number.has_value()); + } +} // namespace Utility diff --git a/tests/UnitTest/main.cpp b/tests/UnitTest/main.cpp new file mode 100644 index 0000000..dd9512c --- /dev/null +++ b/tests/UnitTest/main.cpp @@ -0,0 +1,9 @@ +#include + +int wmain(int argc, wchar_t* argv[]) +{ + testing::InitGoogleTest(&argc, argv); + int retVal = RUN_ALL_TESTS(); + + return retVal; +}