/ src / modules / powerrename / unittests / PowerRenameRegExTests.cpp
PowerRenameRegExTests.cpp
  1  #include "pch.h"
  2  #include "powerrename/lib/Settings.h"
  3  #include <PowerRenameInterfaces.h>
  4  #include <PowerRenameRegEx.h>
  5  #include "MockPowerRenameRegExEvents.h"
  6  
  7  using namespace Microsoft::VisualStudio::CppUnitTestFramework;
  8  
  9  namespace PowerRenameRegExTests
 10  {
 11      TEST_CLASS(SimpleTests){
 12          public:
 13  TEST_CLASS_INITIALIZE(ClassInitialize)
 14  {
 15      CSettingsInstance().SetUseBoostLib(false);
 16  }
 17  
 18  #define TESTS_PARTIAL
 19  #include "CommonRegExTests.h"
 20  
 21  TEST_METHOD(VerifyMatchAllWildcardUseRegEx)
 22  {
 23      CComPtr<IPowerRenameRegEx> renameRegEx;
 24      Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK);
 25      DWORD flags = MatchAllOccurrences | UseRegularExpressions;
 26      Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK);
 27  
 28      SearchReplaceExpected sreTable[] = {
 29          { L".*", L"Foo", L"AAAAAA", L"Foo" },
 30      };
 31  
 32      for (int i = 0; i < ARRAYSIZE(sreTable); i++)
 33      {
 34          PWSTR result = nullptr;
 35          Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK);
 36          Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK);
 37          unsigned long index = {};
 38          Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK);
 39          Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0);
 40          CoTaskMemFree(result);
 41      }
 42  }
 43  
 44  TEST_METHOD(VerifyReplaceFirstWildCardUseRegexMatchAllOccurrences)
 45  {
 46      SearchReplaceExpected sreTable[] = {
 47          //search, replace, test, result
 48          { L".*", L"Foo", L"AAAAAA", L"Foo" },
 49      };
 50      VerifyReplaceFirstWildcard(sreTable, ARRAYSIZE(sreTable), UseRegularExpressions | MatchAllOccurrences);
 51  }
 52  
 53  TEST_METHOD(VerifyHandleCapturingGroups)
 54  {
 55      CComPtr<IPowerRenameRegEx> renameRegEx;
 56      Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK);
 57      DWORD flags = MatchAllOccurrences | UseRegularExpressions | CaseSensitive;
 58      Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK);
 59  
 60      SearchReplaceExpected sreTable[] = {
 61          //search, replace, test, result
 62          { L"(foo)(bar)", L"$1_$002_$223_$001021_$00001", L"foobar", L"foo_$002_bar23_$001021_$00001" },
 63          { L"(foo)(bar)", L"_$1$2_$123$040", L"foobar", L"_foobar_foo23$040" },
 64          { L"(foo)(bar)", L"$$$1", L"foobar", L"$foo" },
 65          { L"(foo)(bar)", L"$$1", L"foobar", L"$1" },
 66          { L"(foo)(bar)", L"$12", L"foobar", L"foo2" },
 67          { L"(foo)(bar)", L"$10", L"foobar", L"foo0" },
 68          { L"(foo)(bar)", L"$01", L"foobar", L"$01" },
 69          { L"(foo)(bar)", L"$$$11", L"foobar", L"$foo1" },
 70          { L"(foo)(bar)", L"$$$$113a", L"foobar", L"$$113a" },
 71      };
 72  
 73      for (int i = 0; i < ARRAYSIZE(sreTable); i++)
 74      {
 75          PWSTR result = nullptr;
 76          Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK);
 77          Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK);
 78          unsigned long index = {};
 79          Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK);
 80          Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0);
 81          CoTaskMemFree(result);
 82      }
 83  }
 84  
 85  TEST_METHOD (VerifyFileAttributesNoPadding)
 86  {
 87      CComPtr<IPowerRenameRegEx> renameRegEx;
 88      Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK);
 89      DWORD flags = MatchAllOccurrences | UseRegularExpressions ;
 90      SYSTEMTIME fileTime = SYSTEMTIME{ 2020, 7, 3, 22, 15, 6, 42, 453 };
 91      Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK);
 92  
 93      SearchReplaceExpected sreTable[] = {
 94          //search, replace, test, result
 95          { L"foo", L"bar$YY-$M-$D-$h-$m-$s-$f", L"foo", L"bar20-7-22-15-6-42-4" },
 96      };
 97  
 98      for (int i = 0; i < ARRAYSIZE(sreTable); i++)
 99      {
100          PWSTR result = nullptr;
101          Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK);
102          Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK);
103          Assert::IsTrue(renameRegEx->PutFileTime(fileTime) == S_OK);
104          unsigned long index = {};
105          Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK);
106          Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0);
107          CoTaskMemFree(result);
108      }
109  }
110  
111  TEST_METHOD (VerifyFileAttributesPadding)
112  {
113      CComPtr<IPowerRenameRegEx> renameRegEx;
114      Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK);
115      DWORD flags = MatchAllOccurrences | UseRegularExpressions;
116      Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK);
117      SYSTEMTIME fileTime = SYSTEMTIME{ 2020, 7, 3, 22, 15, 6, 42, 453 };
118      SearchReplaceExpected sreTable[] = {
119          //search, replace, test, result
120          { L"foo", L"bar$YYYY-$MM-$DD-$hh-$mm-$ss-$fff", L"foo", L"bar2020-07-22-15-06-42-453" },
121      };
122  
123      for (int i = 0; i < ARRAYSIZE(sreTable); i++)
124      {
125          PWSTR result = nullptr;
126          Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK);
127          Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK);
128          Assert::IsTrue(renameRegEx->PutFileTime(fileTime) == S_OK);
129          unsigned long index = {};
130          Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK);
131          Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0);
132          CoTaskMemFree(result);
133      }
134  }
135  
136  TEST_METHOD (VerifyFileAttributesMonthAndDayNames)
137  {
138      CComPtr<IPowerRenameRegEx> renameRegEx;
139      Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK);
140      DWORD flags = MatchAllOccurrences | UseRegularExpressions;
141      Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK);
142  
143      std::locale::global(std::locale(""));
144      SYSTEMTIME fileTime = { 2020, 1, 3, 1, 15, 6, 42, 453 };
145      wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
146      wchar_t dest[MAX_PATH] = L"bar";
147      wchar_t formattedDate[MAX_PATH];
148      if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0)
149          StringCchCopy(localeName, LOCALE_NAME_MAX_LENGTH, L"en_US");
150  
151      GetDateFormatEx(localeName, NULL, &fileTime, L"MMM", formattedDate, MAX_PATH, NULL);
152      formattedDate[0] = towupper(formattedDate[0]);
153      StringCchPrintf(dest, MAX_PATH, TEXT("%s%s"), dest, formattedDate);
154  
155      GetDateFormatEx(localeName, NULL, &fileTime, L"MMMM", formattedDate, MAX_PATH, NULL);
156      formattedDate[0] = towupper(formattedDate[0]);
157      StringCchPrintf(dest, MAX_PATH, TEXT("%s-%s"), dest, formattedDate);
158  
159      GetDateFormatEx(localeName, NULL, &fileTime, L"ddd", formattedDate, MAX_PATH, NULL);
160      formattedDate[0] = towupper(formattedDate[0]);
161      StringCchPrintf(dest, MAX_PATH, TEXT("%s-%s"), dest, formattedDate);
162  
163      GetDateFormatEx(localeName, NULL, &fileTime, L"dddd", formattedDate, MAX_PATH, NULL);
164      formattedDate[0] = towupper(formattedDate[0]);
165      StringCchPrintf(dest, MAX_PATH, TEXT("%s-%s"), dest, formattedDate);
166  
167      SearchReplaceExpected sreTable[] = {
168          //search, replace, test, result
169          { L"foo", L"bar$MMM-$MMMM-$DDD-$DDDD", L"foo", dest },
170      };
171  
172      for (int i = 0; i < ARRAYSIZE(sreTable); i++)
173      {
174          PWSTR result = nullptr;
175          Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK);
176          Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK);
177          Assert::IsTrue(renameRegEx->PutFileTime(fileTime) == S_OK);
178          unsigned long index = {};
179          Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK);
180          Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0);
181          CoTaskMemFree(result);
182      }
183  }
184  
185  TEST_METHOD(VerifyLookbehindFails)
186  {
187      // Standard Library Regex Engine does not support lookbehind, thus test should fail.
188      SearchReplaceExpected sreTable[] = {
189          //search, replace, test, result
190          { L"(?<=E12).*", L"Foo", L"AAAAAA", nullptr },
191          { L"(?<!E12).*", L"Foo", L"AAAAAA", nullptr },
192      };
193  
194      CComPtr<IPowerRenameRegEx> renameRegEx;
195      Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK);
196      Assert::IsTrue(renameRegEx->PutFlags(UseRegularExpressions) == S_OK);
197  
198      for (int i = 0; i < ARRAYSIZE(sreTable); i++)
199      {
200          PWSTR result = nullptr;
201          Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK);
202          Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK);
203          unsigned long index = {};
204          Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == E_FAIL);
205          Assert::AreEqual(sreTable[i].expected, result);
206          CoTaskMemFree(result);
207      }
208  }
209  
210  TEST_METHOD (Verify12and24HourTimeFormats)
211  {
212      CComPtr<IPowerRenameRegEx> renameRegEx;
213      Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK);
214      DWORD flags = MatchAllOccurrences | UseRegularExpressions;
215      Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK);
216  
217      struct TimeTestCase {
218          SYSTEMTIME time;        // Input time
219          PCWSTR formatString;    // Format pattern
220          PCWSTR expectedResult;  // Expected output
221          PCWSTR description;     // Description of what we're testing
222      };
223  
224      struct TimeTestCase testCases[] = {
225          // Midnight (00:00 / 12:00 AM)
226          { { 2025, 4, 4, 10, 0, 0, 0, 0 }, L"[$hh:$mm] [$H:$mm $tt]", L"[00:00] [12:00 am]", L"Midnight formatting" },
227  
228          // Noon (12:00 / 12:00 PM)
229          { { 2025, 4, 4, 10, 12, 0, 0, 0 }, L"[$hh:$mm] [$H:$mm $tt]", L"[12:00] [12:00 pm]", L"Noon formatting" },
230  
231          // 1:05 AM
232          { { 2025, 4, 4, 10, 1, 5, 0, 0 }, L"[$h:$m] [$H:$m $tt] [$hh:$mm] [$HH:$mm $TT]", 
233            L"[1:5] [1:5 am] [01:05] [01:05 AM]", L"1 AM with various formats" },
234  
235          // 11 PM
236          { { 2025, 4, 4, 10, 23, 45, 0, 0 }, L"[$h:$m] [$H:$m $tt] [$hh:$mm] [$HH:$mm $TT]", 
237            L"[23:45] [11:45 pm] [23:45] [11:45 PM]", L"11 PM with various formats" },
238  
239          // Mixed formats in complex pattern
240          { { 2025, 4, 4, 10, 14, 30, 0, 0 }, L"Date: $YYYY-$MM-$DD Time: $hh:$mm (24h) / $H:$mm $tt (12h)", 
241            L"Date: 2025-04-10 Time: 14:30 (24h) / 2:30 pm (12h)", L"Complex combined format" },
242      };
243  
244      for (int i = 0; i < ARRAYSIZE(testCases); i++)
245      {
246          PWSTR result = nullptr;
247          Assert::IsTrue(renameRegEx->PutSearchTerm(L"test") == S_OK);
248          Assert::IsTrue(renameRegEx->PutReplaceTerm(testCases[i].formatString) == S_OK);
249          Assert::IsTrue(renameRegEx->PutFileTime(testCases[i].time) == S_OK);
250          unsigned long index = {};
251          Assert::IsTrue(renameRegEx->Replace(L"test", &result, index) == S_OK);
252          Assert::IsTrue(wcscmp(result, testCases[i].expectedResult) == 0, 
253                         (std::wstring(L"Failed test case: ") + testCases[i].description).c_str());
254          CoTaskMemFree(result);
255      }
256  }
257  
258  };
259  }