diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 7619c20cef..e8af95b8ed 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -2003,6 +2003,7 @@ unregistering unremapped unsubscribe unvirtualized +unwide UOffset UOI Updatelayout diff --git a/src/common/utils/string_utils.h b/src/common/utils/string_utils.h index 27bf64b15b..cd209eb5aa 100644 --- a/src/common/utils/string_utils.h +++ b/src/common/utils/string_utils.h @@ -54,3 +54,12 @@ inline void replace_chars(std::basic_string& s, std::replace(begin(s), end(s), c, replacement_char); } } + +inline std::string unwide(const std::wstring& wide) +{ + std::string result(wide.length(), 0); + std::transform(begin(wide), end(wide), result.begin(), [](const wchar_t c) { + return static_cast(c); + }); + return result; +} diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.idl b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.idl index c488560308..c6b86bfb77 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.idl +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.idl @@ -14,6 +14,7 @@ namespace PowerRenameUI ExplorerItemsSource ExplorerItems { get; }; Windows.Foundation.Collections.IObservableVector SearchRegExShortcuts { get; }; Windows.Foundation.Collections.IObservableVector DateTimeShortcuts { get; }; + Windows.Foundation.Collections.IObservableVector CounterShortcuts { get; }; String OriginalCount; String RenamedCount; diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml index 30d21d105a..cecbeea866 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml @@ -323,6 +323,8 @@ + + @@ -364,7 +366,45 @@ - + + + + + + + + + + + + + + + + + diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.cpp b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.cpp index b80dade90c..4802f820fc 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.cpp +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.cpp @@ -196,6 +196,13 @@ namespace winrt::PowerRenameUI::implementation m_dateTimeShortcuts.Append(winrt::make(L"$ff", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_MilliSeconds2D").ValueAsString())); m_dateTimeShortcuts.Append(winrt::make(L"$f", manager.MainResourceMap().GetValue(L"Resources/DateTimeCheatSheet_MilliSeconds1D").ValueAsString())); + m_CounterShortcuts = winrt::single_threaded_observable_vector(); + m_CounterShortcuts.Append(winrt::make(L"${}", manager.MainResourceMap().GetValue(L"Resources/CounterCheatSheet_Simple").ValueAsString())); + m_CounterShortcuts.Append(winrt::make(L"${start=10}", manager.MainResourceMap().GetValue(L"Resources/CounterCheatSheet_Start").ValueAsString())); + m_CounterShortcuts.Append(winrt::make(L"${increment=5}", manager.MainResourceMap().GetValue(L"Resources/CounterCheatSheet_Increment").ValueAsString())); + m_CounterShortcuts.Append(winrt::make(L"${padding=8}", manager.MainResourceMap().GetValue(L"Resources/CounterCheatSheet_Padding").ValueAsString())); + m_CounterShortcuts.Append(winrt::make(L"${increment=3,padding=4,start=900}", manager.MainResourceMap().GetValue(L"Resources/CounterCheatSheet_Complex").ValueAsString())); + InitializeComponent(); listView_ExplorerItems().ApplyTemplate(); diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.h b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.h index 70759e3ff7..88243af06d 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.h +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.h @@ -81,6 +81,8 @@ namespace winrt::PowerRenameUI::implementation PowerRenameUI::ExplorerItemsSource ExplorerItems() { return m_explorerItems; } winrt::Windows::Foundation::Collections::IObservableVector SearchRegExShortcuts() { return m_searchRegExShortcuts; } winrt::Windows::Foundation::Collections::IObservableVector DateTimeShortcuts() { return m_dateTimeShortcuts; } + winrt::Windows::Foundation::Collections::IObservableVector CounterShortcuts() { return m_CounterShortcuts; } + hstring OriginalCount(); void OriginalCount(hstring value); hstring RenamedCount(); @@ -100,6 +102,7 @@ namespace winrt::PowerRenameUI::implementation PowerRenameUI::ExplorerItemsSource m_explorerItems; winrt::Windows::Foundation::Collections::IObservableVector m_searchRegExShortcuts; winrt::Windows::Foundation::Collections::IObservableVector m_dateTimeShortcuts; + winrt::Windows::Foundation::Collections::IObservableVector m_CounterShortcuts; // Used by PowerRenameManagerEvents HRESULT OnRename(_In_ IPowerRenameItem* renameItem); diff --git a/src/modules/powerrename/PowerRenameUILib/Strings/en-us/Resources.resw b/src/modules/powerrename/PowerRenameUILib/Strings/en-us/Resources.resw index fa1e7cf8e9..a07c601c69 100644 --- a/src/modules/powerrename/PowerRenameUILib/Strings/en-us/Resources.resw +++ b/src/modules/powerrename/PowerRenameUILib/Strings/en-us/Resources.resw @@ -150,6 +150,24 @@ Replace with + + Replace using advanced counter syntax. + + + A simple counter that you can use anywhere in a replace string. + + + A counter with a customized start value. + + + A counter with a customized increment value. + + + A counter with a customized number of leading padding zeroes. + + + A counter showing multiple customized properties. + Replace using file creation date and time diff --git a/src/modules/powerrename/lib/Enumerating.cpp b/src/modules/powerrename/lib/Enumerating.cpp new file mode 100644 index 0000000000..1ea275720c --- /dev/null +++ b/src/modules/powerrename/lib/Enumerating.cpp @@ -0,0 +1,46 @@ +#include + +#include "Enumerating.h" + +#include + +std::vector parseEnumOptions(const std::wstring& replaceWith) +{ + static const std::wregex enumStartRegex(LR"(start=(\d+))"); + static const std::wregex enumIncrementRegex(LR"(increment=(-?\d+))"); + static const std::wregex enumPaddingRegex(LR"(padding=(\d+))"); + + std::string buf; + std::vector options; + std::wregex enumGroupRegex(LR"(\$\{.*?\})"); + for (std::wsregex_iterator i{ begin(replaceWith), end(replaceWith), enumGroupRegex }, end; i != end; ++i) + { + std::wsmatch match = *i; + std::wstring matchString = match.str(); + + EnumOptions option; + option.replaceStrSpan.offset = match.position(); + option.replaceStrSpan.length = match.length(); + + std::wsmatch subMatch; + if (std::regex_search(matchString, subMatch, enumStartRegex)) + { + buf = unwide(subMatch[1].str()); + std::from_chars(buf.data(), buf.data() + buf.size(), option.start.emplace()); + } + if (std::regex_search(matchString, subMatch, enumIncrementRegex)) + { + buf = unwide(subMatch[1].str()); + std::from_chars(buf.data(), buf.data() + buf.size(), option.increment.emplace()); + } + if (std::regex_search(matchString, subMatch, enumPaddingRegex)) + { + buf = unwide(subMatch[1].str()); + std::from_chars(buf.data(), buf.data() + buf.size(), option.padding.emplace()); + } + + options.emplace_back(std::move(option)); + } + + return options; +} diff --git a/src/modules/powerrename/lib/Enumerating.h b/src/modules/powerrename/lib/Enumerating.h new file mode 100644 index 0000000000..7318daa66d --- /dev/null +++ b/src/modules/powerrename/lib/Enumerating.h @@ -0,0 +1,55 @@ +#pragma once + +#include "pch.h" + +struct EnumSpan +{ + size_t offset = 0; + size_t length = 0; +}; + +struct EnumOptions +{ + std::optional start; + std::optional increment; + std::optional padding; + + EnumSpan replaceStrSpan; + + std::strong_ordering operator<=>(const EnumOptions& rhs) const noexcept + { + return std::make_tuple(start, increment, padding) <=> std::make_tuple(rhs.start, rhs.increment, rhs.padding); + } + + bool operator==(const EnumOptions& rhs) const noexcept + { + return std::make_tuple(start, increment, padding) == std::make_tuple(rhs.start, rhs.increment, rhs.padding); + } +}; + +std::vector parseEnumOptions(const std::wstring& replaceWith); + +struct Enumerator +{ + inline Enumerator(EnumOptions options) : + start{ options.start.value_or(0) }, increment{ options.increment.value_or(1) }, padding{ options.padding.value_or(0) }, replaceStrSpan{ options.replaceStrSpan } + { + } + + inline int32_t enumerate(const unsigned long index) const { return start + static_cast(index * increment); } + + inline size_t printTo(wchar_t* buf, const size_t bufSize, const unsigned long index) const + { + const int32_t enumeratedIndex = enumerate(index); + wchar_t format[32]; + swprintf_s(format, sizeof(format) / sizeof(wchar_t), L"%%0%ud", padding); + return swprintf_s(buf, bufSize, format, enumeratedIndex); + } + + EnumSpan replaceStrSpan; + +private: + int start; + int increment; + uint32_t padding; +}; diff --git a/src/modules/powerrename/lib/Helpers.cpp b/src/modules/powerrename/lib/Helpers.cpp index ecd823e4d7..73991c8e9b 100644 --- a/src/modules/powerrename/lib/Helpers.cpp +++ b/src/modules/powerrename/lib/Helpers.cpp @@ -188,7 +188,7 @@ HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR sour { hr = StringCchCopy(result, cchMax, source); } - } + } else if (flags & Capitalized) { if (!(flags & ExtensionOnly)) @@ -234,15 +234,13 @@ HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR sour return hr; } -bool isFileTimeUsed(_In_ PCWSTR source) +bool isFileTimeUsed(_In_ PCWSTR source) { bool used = false; - std::wstring patterns[] = { L"(([^\\$]|^)(\\$\\$)*)\\$Y", L"(([^\\$]|^)(\\$\\$)*)\\$M", L"(([^\\$]|^)(\\$\\$)*)\\$D", - L"(([^\\$]|^)(\\$\\$)*)\\$h", L"(([^\\$]|^)(\\$\\$)*)\\$m", L"(([^\\$]|^)(\\$\\$)*)\\$s", L"(([^\\$]|^)(\\$\\$)*)\\$f" }; - size_t patternsLength = ARRAYSIZE(patterns); - for (size_t i = 0; !used && i < patternsLength; i++) + static const std::array patterns = { std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$Y" }, std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$M" }, std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$D" }, std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$h" }, std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$m" }, std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$s" }, std::wregex{ L"(([^\\$]|^)(\\$\\$)*)\\$f" } }; + for (size_t i = 0; !used && i < patterns.size(); i++) { - if (std::regex_search(source, std::wregex(patterns[i]))) + if (std::regex_search(source, patterns[i])) { used = true; } @@ -253,7 +251,7 @@ bool isFileTimeUsed(_In_ PCWSTR source) HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SYSTEMTIME fileTime) { std::locale::global(std::locale("")); - HRESULT hr = E_INVALIDARG; + HRESULT hr = E_INVALIDARG; if (source && wcslen(source) > 0) { std::wstring res(source); @@ -274,17 +272,17 @@ HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SY StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", (fileTime.wYear % 10)); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$Y"), replaceTerm); - + GetDateFormatEx(localeName, NULL, &fileTime, L"MMMM", formattedDate, MAX_PATH, NULL); formattedDate[0] = towupper(formattedDate[0]); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$MMMM"), replaceTerm); - + GetDateFormatEx(localeName, NULL, &fileTime, L"MMM", formattedDate, MAX_PATH, NULL); formattedDate[0] = towupper(formattedDate[0]); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$MMM"), replaceTerm); - + StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wMonth); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$MM"), replaceTerm); @@ -295,7 +293,7 @@ HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SY formattedDate[0] = towupper(formattedDate[0]); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$DDDD"), replaceTerm); - + GetDateFormatEx(localeName, NULL, &fileTime, L"ddd", formattedDate, MAX_PATH, NULL); formattedDate[0] = towupper(formattedDate[0]); StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%s"), L"$01", formattedDate); @@ -328,10 +326,10 @@ HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SY StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%03d"), L"$01", fileTime.wMilliseconds); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$fff"), replaceTerm); - StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wMilliseconds/10); + StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%02d"), L"$01", fileTime.wMilliseconds / 10); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$ff"), replaceTerm); - StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wMilliseconds/100); + StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%s%d"), L"$01", fileTime.wMilliseconds / 100); res = regex_replace(res, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$f"), replaceTerm); hr = StringCchCopy(result, cchMax, res.c_str()); diff --git a/src/modules/powerrename/lib/PowerRenameInterfaces.h b/src/modules/powerrename/lib/PowerRenameInterfaces.h index 6fcc25368d..974fee5425 100644 --- a/src/modules/powerrename/lib/PowerRenameInterfaces.h +++ b/src/modules/powerrename/lib/PowerRenameInterfaces.h @@ -58,7 +58,7 @@ public: IFACEMETHOD(PutFlags)(_In_ DWORD flags) = 0; IFACEMETHOD(PutFileTime)(_In_ SYSTEMTIME fileTime) = 0; IFACEMETHOD(ResetFileTime)() = 0; - IFACEMETHOD(Replace)(_In_ PCWSTR source, _Outptr_ PWSTR* result) = 0; + IFACEMETHOD(Replace)(_In_ PCWSTR source, _Outptr_ PWSTR* result, unsigned long& enumIndex) = 0; }; interface __declspec(uuid("C7F59201-4DE1-4855-A3A2-26FC3279C8A5")) IPowerRenameItem : public IUnknown diff --git a/src/modules/powerrename/lib/PowerRenameLib.vcxproj b/src/modules/powerrename/lib/PowerRenameLib.vcxproj index be76c5f5ab..e330dfb3e2 100644 --- a/src/modules/powerrename/lib/PowerRenameLib.vcxproj +++ b/src/modules/powerrename/lib/PowerRenameLib.vcxproj @@ -31,6 +31,7 @@ + @@ -47,6 +48,7 @@ + diff --git a/src/modules/powerrename/lib/PowerRenameManager.cpp b/src/modules/powerrename/lib/PowerRenameManager.cpp index 379e349e02..b6641374ba 100644 --- a/src/modules/powerrename/lib/PowerRenameManager.cpp +++ b/src/modules/powerrename/lib/PowerRenameManager.cpp @@ -923,7 +923,7 @@ DWORD WINAPI CPowerRenameManager::s_regexWorkerThread(_In_ void* pv) winrt::check_hresult(pwtd->spsrm->GetRenameRegEx(&spRenameRegEx)); UINT itemCount = 0; - unsigned long itemEnumIndex = 1; + unsigned long itemEnumIndex = 0; winrt::check_hresult(pwtd->spsrm->GetItemCount(&itemCount)); for (UINT u = 0; u < itemCount; u++) diff --git a/src/modules/powerrename/lib/PowerRenameRegEx.cpp b/src/modules/powerrename/lib/PowerRenameRegEx.cpp index ec374b192d..8fdeb67cf1 100644 --- a/src/modules/powerrename/lib/PowerRenameRegEx.cpp +++ b/src/modules/powerrename/lib/PowerRenameRegEx.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "PowerRenameRegEx.h" +#include "Enumerating.h" #include "Settings.h" #include #include @@ -7,15 +8,17 @@ #include #include -using namespace std; +using std::conditional_t; using std::regex_error; -IFACEMETHODIMP_(ULONG) CPowerRenameRegEx::AddRef() +IFACEMETHODIMP_(ULONG) +CPowerRenameRegEx::AddRef() { return InterlockedIncrement(&m_refCount); } -IFACEMETHODIMP_(ULONG) CPowerRenameRegEx::Release() +IFACEMETHODIMP_(ULONG) +CPowerRenameRegEx::Release() { long refCount = InterlockedDecrement(&m_refCount); @@ -127,6 +130,26 @@ IFACEMETHODIMP CPowerRenameRegEx::GetReplaceTerm(_Outptr_ PWSTR* replaceTerm) return hr; } +HRESULT CPowerRenameRegEx::_OnEnumerateItemsChanged() +{ + m_enumerators.clear(); + const auto options = parseEnumOptions(m_RawReplaceTerm); + for (const auto e : options) + m_enumerators.emplace_back(e); + + m_replaceWithEnumeratorOffsets.clear(); + std::wstring replaceWith{ m_RawReplaceTerm }; + // Remove counter expressions and calculate their offsets in replaceWith string. + int32_t offset = 0; + for (const auto& e : options) + { + replaceWith.erase(e.replaceStrSpan.offset + offset, e.replaceStrSpan.length); + m_replaceWithEnumeratorOffsets.push_back(offset); + offset -= static_cast(e.replaceStrSpan.length); + } + return SHStrDup(replaceWith.data(), &m_replaceTerm); +} + IFACEMETHODIMP CPowerRenameRegEx::PutReplaceTerm(_In_ PCWSTR replaceTerm, bool forceRenaming) { bool changed = false || forceRenaming; @@ -134,11 +157,16 @@ IFACEMETHODIMP CPowerRenameRegEx::PutReplaceTerm(_In_ PCWSTR replaceTerm, bool f if (replaceTerm) { CSRWExclusiveAutoLock lock(&m_lock); - if (m_replaceTerm == nullptr || lstrcmp(replaceTerm, m_replaceTerm) != 0) + if (m_replaceTerm == nullptr || lstrcmp(replaceTerm, m_RawReplaceTerm.c_str()) != 0) { changed = true; CoTaskMemFree(m_replaceTerm); - hr = SHStrDup(replaceTerm, &m_replaceTerm); + m_RawReplaceTerm = replaceTerm; + + if (m_flags & EnumerateItems) + hr = _OnEnumerateItemsChanged(); + else + hr = SHStrDup(replaceTerm, &m_replaceTerm); } } @@ -160,7 +188,20 @@ IFACEMETHODIMP CPowerRenameRegEx::PutFlags(_In_ DWORD flags) { if (m_flags != flags) { + const bool newEnumerate = flags & EnumerateItems; + const bool refreshReplaceTerm = !!(m_flags & EnumerateItems) != newEnumerate; m_flags = flags; + if (refreshReplaceTerm) + { + CSRWExclusiveAutoLock lock(&m_lock); + if (newEnumerate) + _OnEnumerateItemsChanged(); + else + { + CoTaskMemFree(m_replaceTerm); + SHStrDup(m_RawReplaceTerm.c_str(), &m_replaceTerm); + } + } _OnFlagsChanged(); } return S_OK; @@ -202,7 +243,7 @@ HRESULT CPowerRenameRegEx::s_CreateInstance(_Outptr_ IPowerRenameRegEx** renameR { *renameRegEx = nullptr; - CPowerRenameRegEx *newRenameRegEx = new CPowerRenameRegEx(); + CPowerRenameRegEx* newRenameRegEx = new CPowerRenameRegEx(); HRESULT hr = E_OUTOFMEMORY; if (newRenameRegEx) { @@ -228,7 +269,20 @@ CPowerRenameRegEx::~CPowerRenameRegEx() CoTaskMemFree(m_replaceTerm); } -HRESULT CPowerRenameRegEx::Replace(_In_ PCWSTR source, _Outptr_ PWSTR* result) +template, class Options = decltype(Regex::icase)> +static std::wstring RegexReplaceEx(const std::wstring& source, const std::wstring& searchTerm, const std::wstring& replaceTerm, const bool matchAll, const bool caseInsensitive) +{ + Regex pattern(searchTerm, Options::ECMAScript | (caseInsensitive ? Options::icase : Options{})); + + using Flags = conditional_t; + const auto flags = matchAll ? Flags::match_default : Flags::format_first_only; + + return regex_replace(source, pattern, replaceTerm, flags); +} + +static constexpr std::array RegexReplaceDispatch = { RegexReplaceEx, RegexReplaceEx }; + +HRESULT CPowerRenameRegEx::Replace(_In_ PCWSTR source, _Outptr_ PWSTR* result, unsigned long& enumIndex) { *result = nullptr; @@ -238,7 +292,7 @@ HRESULT CPowerRenameRegEx::Replace(_In_ PCWSTR source, _Outptr_ PWSTR* result) { return hr; } - wstring res = source; + std::wstring res = source; try { // TODO: creating the regex could be costly. May want to cache this. @@ -250,47 +304,49 @@ HRESULT CPowerRenameRegEx::Replace(_In_ PCWSTR source, _Outptr_ PWSTR* result) fileTimeErrorOccurred = true; } - std::wstring sourceToUse(source); + std::wstring sourceToUse; + std::wstring originalSource; + sourceToUse.reserve(MAX_PATH); + originalSource.reserve(MAX_PATH); + sourceToUse = source; + originalSource = sourceToUse; + std::wstring searchTerm(m_searchTerm); - std::wstring replaceTerm(L""); + std::wstring replaceTerm; if (m_useFileTime && !fileTimeErrorOccurred) { - replaceTerm = wstring(newReplaceTerm); + replaceTerm = newReplaceTerm; } else if (m_replaceTerm) { - replaceTerm = wstring(m_replaceTerm); + replaceTerm = m_replaceTerm; } - replaceTerm = regex_replace(replaceTerm, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$[0]"), L"$1$$$0"); - replaceTerm = regex_replace(replaceTerm, std::wregex(L"(([^\\$]|^)(\\$\\$)*)\\$([1-9])"), L"$1$0$4"); + static const std::wregex zeroGroupRegex(L"(([^\\$]|^)(\\$\\$)*)\\$[0]"); + static const std::wregex otherGroupsRegex(L"(([^\\$]|^)(\\$\\$)*)\\$([1-9])"); + if (m_flags & EnumerateItems) + { + std::array buffer; + int32_t offset = 0; + + for (size_t ei = 0; ei < m_enumerators.size(); ++ei) + { + const auto& e = m_enumerators[ei]; + const auto replacementLength = static_cast(e.printTo(buffer.data(), buffer.size(), enumIndex)); + replaceTerm.insert(e.replaceStrSpan.offset + offset + m_replaceWithEnumeratorOffsets[ei], buffer.data()); + offset += replacementLength; + } + } + + bool replacedSomething = false; if (m_flags & UseRegularExpressions) { - if (_useBoostLib) - { - boost::wregex pattern(m_searchTerm, (!(m_flags & CaseSensitive)) ? boost::regex::icase | boost::regex::ECMAScript : boost::regex::ECMAScript); - if (m_flags & MatchAllOccurrences) - { - res = boost::regex_replace(wstring(source), pattern, replaceTerm); - } - else - { - res = boost::regex_replace(wstring(source), pattern, replaceTerm, boost::regex_constants::format_first_only); - } - } - else - { - std::wregex pattern(m_searchTerm, (!(m_flags & CaseSensitive)) ? regex_constants::icase | regex_constants::ECMAScript : regex_constants::ECMAScript); - if (m_flags & MatchAllOccurrences) - { - res = regex_replace(wstring(source), pattern, replaceTerm); - } - else - { - res = regex_replace(wstring(source), pattern, replaceTerm, regex_constants::format_first_only); - } - } + replaceTerm = regex_replace(replaceTerm, zeroGroupRegex, L"$1$$$0"); + replaceTerm = regex_replace(replaceTerm, otherGroupsRegex, L"$1$0$4"); + + res = RegexReplaceDispatch[_useBoostLib](source, m_searchTerm, replaceTerm, m_flags & MatchAllOccurrences, !(m_flags & CaseSensitive)); + replacedSomething = originalSource != res; } else { @@ -303,16 +359,17 @@ HRESULT CPowerRenameRegEx::Replace(_In_ PCWSTR source, _Outptr_ PWSTR* result) { res = sourceToUse.replace(pos, searchTerm.length(), replaceTerm); pos += replaceTerm.length(); + replacedSomething = true; } - if (!(m_flags & MatchAllOccurrences)) { break; } } while (pos != std::string::npos); } - hr = SHStrDup(res.c_str(), result); + if (replacedSomething) + enumIndex++; } catch (regex_error e) { diff --git a/src/modules/powerrename/lib/PowerRenameRegEx.h b/src/modules/powerrename/lib/PowerRenameRegEx.h index dae5f04380..1b4e20a713 100644 --- a/src/modules/powerrename/lib/PowerRenameRegEx.h +++ b/src/modules/powerrename/lib/PowerRenameRegEx.h @@ -1,9 +1,8 @@ #pragma once #include "pch.h" -#include -#include #include "srwlock.h" +#include "Enumerating.h" #include "PowerRenameInterfaces.h" #define DEFAULT_FLAGS 0 @@ -12,7 +11,7 @@ class CPowerRenameRegEx : public IPowerRenameRegEx { public: // IUnknown - IFACEMETHODIMP QueryInterface(_In_ REFIID iid, _Outptr_ void** resultInterface); + IFACEMETHODIMP QueryInterface(_In_ REFIID iid, _Outptr_ void** resultInterface); IFACEMETHODIMP_(ULONG) AddRef(); IFACEMETHODIMP_(ULONG) Release(); @@ -27,9 +26,9 @@ public: IFACEMETHODIMP PutFlags(_In_ DWORD flags); IFACEMETHODIMP PutFileTime(_In_ SYSTEMTIME fileTime); IFACEMETHODIMP ResetFileTime(); - IFACEMETHODIMP Replace(_In_ PCWSTR source, _Outptr_ PWSTR* result); + IFACEMETHODIMP Replace(_In_ PCWSTR source, _Outptr_ PWSTR* result, unsigned long& enumIndex); - static HRESULT s_CreateInstance(_Outptr_ IPowerRenameRegEx **renameRegEx); + static HRESULT s_CreateInstance(_Outptr_ IPowerRenameRegEx** renameRegEx); protected: CPowerRenameRegEx(); @@ -39,6 +38,7 @@ protected: void _OnReplaceTermChanged(); void _OnFlagsChanged(); void _OnFileTimeChanged(); + HRESULT _OnEnumerateItemsChanged(); size_t _Find(std::wstring data, std::wstring toSearch, bool caseInsensitive, size_t pos); @@ -46,8 +46,9 @@ protected: DWORD m_flags = DEFAULT_FLAGS; PWSTR m_searchTerm = nullptr; PWSTR m_replaceTerm = nullptr; + std::wstring m_RawReplaceTerm; - SYSTEMTIME m_fileTime = {0}; + SYSTEMTIME m_fileTime = { 0 }; bool m_useFileTime = false; CSRWLock m_lock; @@ -55,6 +56,9 @@ protected: DWORD m_cookie = 0; + std::vector m_enumerators; + std::vector m_replaceWithEnumeratorOffsets; + struct RENAME_REGEX_EVENT { IPowerRenameRegExEvents* pEvents; diff --git a/src/modules/powerrename/lib/Renaming.cpp b/src/modules/powerrename/lib/Renaming.cpp index ee070bb831..bf27500529 100644 --- a/src/modules/powerrename/lib/Renaming.cpp +++ b/src/modules/powerrename/lib/Renaming.cpp @@ -86,7 +86,7 @@ bool DoRename(CComPtr& spRenameRegEx, unsigned long& itemEnum // Failure here means we didn't match anything or had nothing to match // Call put_newName with null in that case to reset it - winrt::check_hresult(spRenameRegEx->Replace(sourceName, &newName)); + winrt::check_hresult(spRenameRegEx->Replace(sourceName, &newName, itemEnumIndex)); if (useFileTime) { @@ -166,17 +166,6 @@ bool DoRename(CComPtr& spRenameRegEx, unsigned long& itemEnum newNameToUse = nullptr; } - wchar_t uniqueName[MAX_PATH] = { 0 }; - if (newNameToUse != nullptr && (flags & EnumerateItems)) - { - unsigned long countUsed = 0; - if (GetEnumeratedFileName(uniqueName, ARRAYSIZE(uniqueName), newNameToUse, nullptr, itemEnumIndex, &countUsed)) - { - newNameToUse = uniqueName; - } - itemEnumIndex++; - } - spItem->PutStatus(PowerRenameItemRenameStatus::ShouldRename); if (newNameToUse != nullptr) { diff --git a/src/modules/powerrename/lib/pch.h b/src/modules/powerrename/lib/pch.h index 890021fe33..af9f76bbff 100644 --- a/src/modules/powerrename/lib/pch.h +++ b/src/modules/powerrename/lib/pch.h @@ -21,6 +21,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include diff --git a/src/modules/powerrename/unittests/CommonRegExTests.h b/src/modules/powerrename/unittests/CommonRegExTests.h new file mode 100644 index 0000000000..f9cb469fb3 --- /dev/null +++ b/src/modules/powerrename/unittests/CommonRegExTests.h @@ -0,0 +1,472 @@ +//#undef TESTS_PARTIAL // Uncomment temporarily to make intellisense work in this file. +#ifndef TESTS_PARTIAL +#include "CppUnitTestInclude.h" +#include "powerrename/lib/Settings.h" +#include +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; +namespace PowerRenameRegExTests +{ +TEST_CLASS (SimpleTests) +{ +public: +#endif + +struct SearchReplaceExpected +{ + PCWSTR search; + PCWSTR replace; + PCWSTR test; + PCWSTR expected; +}; + +TEST_METHOD (GeneralReplaceTest) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(L"foo") == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(L"big") == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(L"foobar", &result, index) == S_OK); + Assert::AreEqual(L"bigbar", result); + CoTaskMemFree(result); +} + +TEST_METHOD (ReplaceNoMatch) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(L"notfound") == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(L"big") == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(L"foobar", &result, index) == S_OK); + Assert::AreEqual(L"foobar", result); + CoTaskMemFree(result); +} + +TEST_METHOD (ReplaceNoSearchOrReplaceTerm) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + PWSTR result = nullptr; + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(L"foobar", &result, index) == S_OK); + Assert::IsTrue(result == nullptr); + CoTaskMemFree(result); +} + +TEST_METHOD (ReplaceNoReplaceTerm) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(L"foo") == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(L"foobar", &result, index) == S_OK); + Assert::AreEqual(L"bar", result); + CoTaskMemFree(result); +} + +TEST_METHOD (ReplaceEmptyStringReplaceTerm) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(L"foo") == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(L"") == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(L"foobar", &result, index) == S_OK); + Assert::AreEqual(L"bar", result); + CoTaskMemFree(result); +} + +TEST_METHOD (VerifyDefaultFlags) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = 0; + Assert::IsTrue(renameRegEx->GetFlags(&flags) == S_OK); + Assert::IsTrue(flags == 0); +} + +TEST_METHOD (VerifyCaseSensitiveSearch) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = CaseSensitive; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + + SearchReplaceExpected sreTable[] = { + { L"Foo", L"Foo", L"FooBar", L"FooBar" }, + { L"Foo", L"boo", L"FooBar", L"booBar" }, + { L"Foo", L"boo", L"foobar", L"foobar" }, + { L"123", L"654", L"123456", L"654456" }, + }; + + for (int i = 0; i < ARRAYSIZE(sreTable); i++) + { + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); + Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); + CoTaskMemFree(result); + } +} + +TEST_METHOD (VerifyReplaceFirstOnly) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = 0; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + + SearchReplaceExpected sreTable[] = { + { L"B", L"BB", L"ABA", L"ABBA" }, + { L"B", L"A", L"ABBBA", L"AABBA" }, + { L"B", L"BBB", L"ABABAB", L"ABBBABAB" }, + }; + + for (int i = 0; i < ARRAYSIZE(sreTable); i++) + { + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); + Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); + CoTaskMemFree(result); + } +} + +TEST_METHOD (VerifyReplaceAll) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = MatchAllOccurrences; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + + SearchReplaceExpected sreTable[] = { + { L"B", L"BB", L"ABA", L"ABBA" }, + { L"B", L"A", L"ABBBA", L"AAAAA" }, + { L"B", L"BBB", L"ABABAB", L"ABBBABBBABBB" }, + }; + + for (int i = 0; i < ARRAYSIZE(sreTable); i++) + { + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); + Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); + CoTaskMemFree(result); + } +} + +TEST_METHOD (VerifyReplaceAllCaseInsensitive) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = MatchAllOccurrences | CaseSensitive; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + + SearchReplaceExpected sreTable[] = { + { L"B", L"BB", L"ABA", L"ABBA" }, + { L"B", L"A", L"ABBBA", L"AAAAA" }, + { L"B", L"BBB", L"ABABAB", L"ABBBABBBABBB" }, + { L"b", L"BBB", L"AbABAb", L"ABBBABABBB" }, + }; + + for (int i = 0; i < ARRAYSIZE(sreTable); i++) + { + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); + Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); + CoTaskMemFree(result); + } +} + +TEST_METHOD (VerifyReplaceFirstOnlyUseRegEx) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = UseRegularExpressions; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + + SearchReplaceExpected sreTable[] = { + { L"B", L"BB", L"ABA", L"ABBA" }, + { L"B", L"A", L"ABBBA", L"AABBA" }, + { L"B", L"BBB", L"ABABAB", L"ABBBABAB" }, + }; + + for (int i = 0; i < ARRAYSIZE(sreTable); i++) + { + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); + Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); + CoTaskMemFree(result); + } +} + +TEST_METHOD (VerifyReplaceAllUseRegEx) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = MatchAllOccurrences | UseRegularExpressions; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + + SearchReplaceExpected sreTable[] = { + { L"B", L"BB", L"ABA", L"ABBA" }, + { L"B", L"A", L"ABBBA", L"AAAAA" }, + { L"B", L"BBB", L"ABABAB", L"ABBBABBBABBB" }, + }; + + for (int i = 0; i < ARRAYSIZE(sreTable); i++) + { + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); + Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); + CoTaskMemFree(result); + } +} + +TEST_METHOD (VerifyReplaceAllUseRegExCaseSensitive) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = MatchAllOccurrences | UseRegularExpressions | CaseSensitive; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + + SearchReplaceExpected sreTable[] = { + { L"B", L"BB", L"ABA", L"ABBA" }, + { L"B", L"A", L"ABBBA", L"AAAAA" }, + { L"b", L"BBB", L"AbABAb", L"ABBBABABBB" }, + }; + + for (int i = 0; i < ARRAYSIZE(sreTable); i++) + { + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); + Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); + CoTaskMemFree(result); + } +} + +void VerifyReplaceFirstWildcard(SearchReplaceExpected sreTable[], int tableSize, DWORD flags) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + + for (int i = 0; i < tableSize; i++) + { + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); + Assert::AreEqual(sreTable[i].expected, result); + CoTaskMemFree(result); + } +} + +TEST_METHOD (VerifyReplaceFirstWildCardUseRegex) +{ + SearchReplaceExpected sreTable[] = { + //search, replace, test, result + { L".*", L"Foo", L"AAAAAA", L"Foo" }, + }; + VerifyReplaceFirstWildcard(sreTable, ARRAYSIZE(sreTable), UseRegularExpressions); +} + +TEST_METHOD (VerifyReplaceFirstWildCardMatchAllOccurrences) +{ + SearchReplaceExpected sreTable[] = { + //search, replace, test, result + { L".*", L"Foo", L"AAAAAA", L"AAAAAA" }, + { L".*", L"Foo", L".*", L"Foo" }, + { L".*", L"Foo", L".*Bar.*", L"FooBarFoo" }, + }; + VerifyReplaceFirstWildcard(sreTable, ARRAYSIZE(sreTable), MatchAllOccurrences); +} + +TEST_METHOD (VerifyReplaceFirstWildNoFlags) +{ + SearchReplaceExpected sreTable[] = { + //search, replace, test, result + { L".*", L"Foo", L"AAAAAA", L"AAAAAA" }, + { L".*", L"Foo", L".*", L"Foo" }, + }; + VerifyReplaceFirstWildcard(sreTable, ARRAYSIZE(sreTable), 0); +} + +TEST_METHOD (VerifyEventsFire) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + CMockPowerRenameRegExEvents* mockEvents = new CMockPowerRenameRegExEvents(); + CComPtr regExEvents; + Assert::IsTrue(mockEvents->QueryInterface(IID_PPV_ARGS(®ExEvents)) == S_OK); + DWORD cookie = 0; + Assert::IsTrue(renameRegEx->Advise(regExEvents, &cookie) == S_OK); + DWORD flags = MatchAllOccurrences | UseRegularExpressions | CaseSensitive; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + Assert::IsTrue(renameRegEx->PutSearchTerm(L"FOO") == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(L"BAR") == S_OK); + Assert::IsTrue(renameRegEx->PutFileTime(SYSTEMTIME{ 0 }) == S_OK); + Assert::IsTrue(renameRegEx->ResetFileTime() == S_OK); + Assert::IsTrue(lstrcmpi(L"FOO", mockEvents->m_searchTerm) == 0); + Assert::IsTrue(lstrcmpi(L"BAR", mockEvents->m_replaceTerm) == 0); + Assert::IsTrue(flags == mockEvents->m_flags); + Assert::IsTrue(renameRegEx->UnAdvise(cookie) == S_OK); + mockEvents->Release(); +} + +TEST_METHOD (VerifySimpleCounterNoRegex) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = EnumerateItems; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(L"bar") == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(L"$1bar_${}") == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(L"foobar", &result, index) == S_OK); + Assert::AreEqual(L"foo$1bar_0", result); + CoTaskMemFree(result); +} + +TEST_METHOD (VerifySimpleCounterNoEnum) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = UseRegularExpressions; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(L"bar") == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(L"$1bar_${}") == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(L"foobar", &result, index) == S_OK); + Assert::AreEqual(L"foobar_${}", result); + CoTaskMemFree(result); +} + +TEST_METHOD (VerifySimpleCounter) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = EnumerateItems | UseRegularExpressions; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->PutSearchTerm(L"bar") == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(L"bar_${}") == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(L"foobar", &result, index) == S_OK); + Assert::AreEqual(L"foobar_0", result); + CoTaskMemFree(result); +} + +TEST_METHOD (VerifyMultipleCounters) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = EnumerateItems | UseRegularExpressions; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + Assert::IsTrue(renameRegEx->PutSearchTerm(L"bar") == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(L"_${}_bar_${}") == S_OK); + unsigned long index = {}; + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->Replace(L"foobar", &result, index) == S_OK); + Assert::AreEqual(L"foo_0_bar_0", result); + CoTaskMemFree(result); +} + +TEST_METHOD (VerifyCounterIncrementCustomization) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = EnumerateItems | UseRegularExpressions; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + Assert::IsTrue(renameRegEx->PutSearchTerm(L"bar") == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(L"bar_${increment=10}") == S_OK); + unsigned long index = 1; + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->Replace(L"foobar", &result, index) == S_OK); + Assert::AreEqual(L"foobar_10", result); + Assert::AreEqual(index, 2); + Assert::IsTrue(renameRegEx->Replace(L"foobar", &result, index) == S_OK); + Assert::AreEqual(L"foobar_20", result); + Assert::AreEqual(index, 3); + CoTaskMemFree(result); +} + +TEST_METHOD (VerifyCounterStartCustomization) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = EnumerateItems | UseRegularExpressions; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + Assert::IsTrue(renameRegEx->PutSearchTerm(L"bar") == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(L"bar_${start=1000}") == S_OK); + unsigned long index = 5; + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->Replace(L"foobar", &result, index) == S_OK); + Assert::AreEqual(L"foobar_1005", result); + CoTaskMemFree(result); +} + +TEST_METHOD (VerifyCounterPaddingCustomization) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = EnumerateItems | UseRegularExpressions; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + Assert::IsTrue(renameRegEx->PutSearchTerm(L"bar") == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(L"bar_${padding=5}") == S_OK); + unsigned long index = 204; + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->Replace(L"foobar", &result, index) == S_OK); + Assert::AreEqual(L"foobar_00204", result); + CoTaskMemFree(result); +} + +TEST_METHOD (VerifyCounterAllCustomizations) +{ + CComPtr renameRegEx; + Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); + DWORD flags = EnumerateItems | UseRegularExpressions; + Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); + Assert::IsTrue(renameRegEx->PutSearchTerm(L"bar") == S_OK); + Assert::IsTrue(renameRegEx->PutReplaceTerm(L"bar_${increment=7,start=993,padding=5}") == S_OK); + unsigned long index = 12; + PWSTR result = nullptr; + Assert::IsTrue(renameRegEx->Replace(L"foobar", &result, index) == S_OK); + Assert::AreEqual(L"foobar_01077", result); + CoTaskMemFree(result); +} + +#ifndef TESTS_PARTIAL +}; +} +#endif diff --git a/src/modules/powerrename/unittests/CppUnitTestInclude.h b/src/modules/powerrename/unittests/CppUnitTestInclude.h new file mode 100644 index 0000000000..dce5689675 --- /dev/null +++ b/src/modules/powerrename/unittests/CppUnitTestInclude.h @@ -0,0 +1,8 @@ +#pragma once +// Headers for CppUnitTest + +// Suppressing 26466 - Don't use static_cast downcasts - in CppUnitTest.h +#pragma warning(push) +#pragma warning(disable : 26466) +#include "CppUnitTest.h" +#pragma warning(pop) diff --git a/src/modules/powerrename/unittests/PowerRenameLibUnitTests.vcxproj b/src/modules/powerrename/unittests/PowerRenameLibUnitTests.vcxproj index 0598fb77e6..f64a2e85a3 100644 --- a/src/modules/powerrename/unittests/PowerRenameLibUnitTests.vcxproj +++ b/src/modules/powerrename/unittests/PowerRenameLibUnitTests.vcxproj @@ -38,6 +38,7 @@ + @@ -45,6 +46,7 @@ + diff --git a/src/modules/powerrename/unittests/PowerRenameLibUnitTests.vcxproj.filters b/src/modules/powerrename/unittests/PowerRenameLibUnitTests.vcxproj.filters index 8708f3397e..db5bc09af4 100644 --- a/src/modules/powerrename/unittests/PowerRenameLibUnitTests.vcxproj.filters +++ b/src/modules/powerrename/unittests/PowerRenameLibUnitTests.vcxproj.filters @@ -17,9 +17,11 @@ + Header Files + diff --git a/src/modules/powerrename/unittests/PowerRenameRegExBoostTests.cpp b/src/modules/powerrename/unittests/PowerRenameRegExBoostTests.cpp index bb70ebb99b..2bff1a4b9c 100644 --- a/src/modules/powerrename/unittests/PowerRenameRegExBoostTests.cpp +++ b/src/modules/powerrename/unittests/PowerRenameRegExBoostTests.cpp @@ -8,14 +8,6 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace PowerRenameRegExBoostTests { - struct SearchReplaceExpected - { - PCWSTR search; - PCWSTR replace; - PCWSTR test; - PCWSTR expected; - }; - TEST_CLASS(SimpleTests) { public: @@ -29,241 +21,8 @@ TEST_CLASS_CLEANUP(ClassCleanup) CSettingsInstance().SetUseBoostLib(false); } -TEST_METHOD(GeneralReplaceTest) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(L"foo") == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(L"big") == S_OK); - Assert::IsTrue(renameRegEx->Replace(L"foobar", &result) == S_OK); - Assert::IsTrue(wcscmp(result, L"bigbar") == 0); - CoTaskMemFree(result); -} - -TEST_METHOD(ReplaceNoMatch) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(L"notfound") == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(L"big") == S_OK); - Assert::IsTrue(renameRegEx->Replace(L"foobar", &result) == S_OK); - Assert::IsTrue(wcscmp(result, L"foobar") == 0); - CoTaskMemFree(result); -} - -TEST_METHOD(ReplaceNoSearchOrReplaceTerm) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->Replace(L"foobar", &result) == S_OK); - Assert::IsTrue(result == nullptr); - CoTaskMemFree(result); -} - -TEST_METHOD(ReplaceNoReplaceTerm) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(L"foo") == S_OK); - Assert::IsTrue(renameRegEx->Replace(L"foobar", &result) == S_OK); - Assert::IsTrue(wcscmp(result, L"bar") == 0); - CoTaskMemFree(result); -} - -TEST_METHOD(ReplaceEmptyStringReplaceTerm) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(L"foo") == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(L"") == S_OK); - Assert::IsTrue(renameRegEx->Replace(L"foobar", &result) == S_OK); - Assert::IsTrue(wcscmp(result, L"bar") == 0); - CoTaskMemFree(result); -} - -TEST_METHOD(VerifyDefaultFlags) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = 0; - Assert::IsTrue(renameRegEx->GetFlags(&flags) == S_OK); - Assert::IsTrue(flags == 0); -} - -TEST_METHOD(VerifyCaseSensitiveSearch) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = CaseSensitive; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - SearchReplaceExpected sreTable[] = { - { L"Foo", L"Foo", L"FooBar", L"FooBar" }, - { L"Foo", L"boo", L"FooBar", L"booBar" }, - { L"Foo", L"boo", L"foobar", L"foobar" }, - { L"123", L"654", L"123456", L"654456" }, - }; - - for (int i = 0; i < ARRAYSIZE(sreTable); i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); - CoTaskMemFree(result); - } -} - -TEST_METHOD(VerifyReplaceFirstOnly) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = 0; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - SearchReplaceExpected sreTable[] = { - { L"B", L"BB", L"ABA", L"ABBA" }, - { L"B", L"A", L"ABBBA", L"AABBA" }, - { L"B", L"BBB", L"ABABAB", L"ABBBABAB" }, - }; - - for (int i = 0; i < ARRAYSIZE(sreTable); i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); - CoTaskMemFree(result); - } -} - -TEST_METHOD(VerifyReplaceAll) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = MatchAllOccurrences; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - SearchReplaceExpected sreTable[] = { - { L"B", L"BB", L"ABA", L"ABBA" }, - { L"B", L"A", L"ABBBA", L"AAAAA" }, - { L"B", L"BBB", L"ABABAB", L"ABBBABBBABBB" }, - }; - - for (int i = 0; i < ARRAYSIZE(sreTable); i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); - CoTaskMemFree(result); - } -} - -TEST_METHOD(VerifyReplaceAllCaseInsensitive) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = MatchAllOccurrences | CaseSensitive; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - SearchReplaceExpected sreTable[] = { - { L"B", L"BB", L"ABA", L"ABBA" }, - { L"B", L"A", L"ABBBA", L"AAAAA" }, - { L"B", L"BBB", L"ABABAB", L"ABBBABBBABBB" }, - { L"b", L"BBB", L"AbABAb", L"ABBBABABBB" }, - }; - - for (int i = 0; i < ARRAYSIZE(sreTable); i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); - CoTaskMemFree(result); - } -} - -TEST_METHOD(VerifyReplaceFirstOnlyUseRegEx) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = UseRegularExpressions; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - SearchReplaceExpected sreTable[] = { - { L"B", L"BB", L"ABA", L"ABBA" }, - { L"B", L"A", L"ABBBA", L"AABBA" }, - { L"B", L"BBB", L"ABABAB", L"ABBBABAB" }, - }; - - for (int i = 0; i < ARRAYSIZE(sreTable); i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); - CoTaskMemFree(result); - } -} - -TEST_METHOD(VerifyReplaceAllUseRegEx) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = MatchAllOccurrences | UseRegularExpressions; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - SearchReplaceExpected sreTable[] = { - { L"B", L"BB", L"ABA", L"ABBA" }, - { L"B", L"A", L"ABBBA", L"AAAAA" }, - { L"B", L"BBB", L"ABABAB", L"ABBBABBBABBB" }, - }; - - for (int i = 0; i < ARRAYSIZE(sreTable); i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); - CoTaskMemFree(result); - } -} - -TEST_METHOD(VerifyReplaceAllUseRegExCaseSensitive) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = MatchAllOccurrences | UseRegularExpressions | CaseSensitive; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - SearchReplaceExpected sreTable[] = { - { L"B", L"BB", L"ABA", L"ABBA" }, - { L"B", L"A", L"ABBBA", L"AAAAA" }, - { L"b", L"BBB", L"AbABAb", L"ABBBABABBB" }, - }; - - for (int i = 0; i < ARRAYSIZE(sreTable); i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); - CoTaskMemFree(result); - } -} +#define TESTS_PARTIAL +#include "CommonRegExTests.h" TEST_METHOD(VerifyMatchAllWildcardUseRegEx) { @@ -284,38 +43,13 @@ TEST_METHOD(VerifyMatchAllWildcardUseRegEx) PWSTR result = nullptr; Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); CoTaskMemFree(result); } } -void VerifyReplaceFirstWildcard(SearchReplaceExpected sreTable[], int tableSize, DWORD flags) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - for (int i = 0; i < tableSize; i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::AreEqual(sreTable[i].expected, result); - CoTaskMemFree(result); - } -} - -TEST_METHOD(VerifyReplaceFirstWildCardUseRegex) -{ - SearchReplaceExpected sreTable[] = { - //search, replace, test, result - { L".*", L"Foo", L"AAAAAA", L"Foo" }, - }; - VerifyReplaceFirstWildcard(sreTable, ARRAYSIZE(sreTable), UseRegularExpressions); -} - TEST_METHOD(VerifyReplaceFirstWildCardUseRegexMatchAllOccurrences) { // This differs from the Standard Library: .* has two matches (all and nothing). @@ -327,27 +61,6 @@ TEST_METHOD(VerifyReplaceFirstWildCardUseRegexMatchAllOccurrences) VerifyReplaceFirstWildcard(sreTable, ARRAYSIZE(sreTable), UseRegularExpressions | MatchAllOccurrences); } -TEST_METHOD(VerifyReplaceFirstWildCardMatchAllOccurrences) -{ - SearchReplaceExpected sreTable[] = { - //search, replace, test, result - { L".*", L"Foo", L"AAAAAA", L"AAAAAA" }, - { L".*", L"Foo", L".*", L"Foo" }, - { L".*", L"Foo", L".*Bar.*", L"FooBarFoo" }, - }; - VerifyReplaceFirstWildcard(sreTable, ARRAYSIZE(sreTable), MatchAllOccurrences); -} - -TEST_METHOD(VerifyReplaceFirstWildNoFlags) -{ - SearchReplaceExpected sreTable[] = { - //search, replace, test, result - { L".*", L"Foo", L"AAAAAA", L"AAAAAA" }, - { L".*", L"Foo", L".*", L"Foo" }, - }; - VerifyReplaceFirstWildcard(sreTable, ARRAYSIZE(sreTable), 0); -} - TEST_METHOD(VerifyHandleCapturingGroups) { // This differs from the Standard Library: Boost does not recognize $123 as $1 and "23". @@ -380,7 +93,8 @@ TEST_METHOD(VerifyHandleCapturingGroups) PWSTR result = nullptr; Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); CoTaskMemFree(result); } @@ -407,32 +121,11 @@ TEST_METHOD(VerifyLookbehind) PWSTR result = nullptr; Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); CoTaskMemFree(result); } } - -TEST_METHOD(VerifyEventsFire) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - CMockPowerRenameRegExEvents* mockEvents = new CMockPowerRenameRegExEvents(); - CComPtr regExEvents; - Assert::IsTrue(mockEvents->QueryInterface(IID_PPV_ARGS(®ExEvents)) == S_OK); - DWORD cookie = 0; - Assert::IsTrue(renameRegEx->Advise(regExEvents, &cookie) == S_OK); - DWORD flags = MatchAllOccurrences | UseRegularExpressions | CaseSensitive; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - Assert::IsTrue(renameRegEx->PutSearchTerm(L"FOO") == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(L"BAR") == S_OK); - Assert::IsTrue(renameRegEx->PutFileTime(SYSTEMTIME{0}) == S_OK); - Assert::IsTrue(renameRegEx->ResetFileTime() == S_OK); - Assert::IsTrue(lstrcmpi(L"FOO", mockEvents->m_searchTerm) == 0); - Assert::IsTrue(lstrcmpi(L"BAR", mockEvents->m_replaceTerm) == 0); - Assert::IsTrue(flags == mockEvents->m_flags); - Assert::IsTrue(renameRegEx->UnAdvise(cookie) == S_OK); - mockEvents->Release(); -} }; } diff --git a/src/modules/powerrename/unittests/PowerRenameRegExTests.cpp b/src/modules/powerrename/unittests/PowerRenameRegExTests.cpp index aadfb99c1c..eff80bf4cd 100644 --- a/src/modules/powerrename/unittests/PowerRenameRegExTests.cpp +++ b/src/modules/powerrename/unittests/PowerRenameRegExTests.cpp @@ -8,14 +8,6 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace PowerRenameRegExTests { - struct SearchReplaceExpected - { - PCWSTR search; - PCWSTR replace; - PCWSTR test; - PCWSTR expected; - }; - TEST_CLASS(SimpleTests){ public: TEST_CLASS_INITIALIZE(ClassInitialize) @@ -23,241 +15,8 @@ TEST_CLASS_INITIALIZE(ClassInitialize) CSettingsInstance().SetUseBoostLib(false); } -TEST_METHOD(GeneralReplaceTest) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(L"foo") == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(L"big") == S_OK); - Assert::IsTrue(renameRegEx->Replace(L"foobar", &result) == S_OK); - Assert::IsTrue(wcscmp(result, L"bigbar") == 0); - CoTaskMemFree(result); -} - -TEST_METHOD(ReplaceNoMatch) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(L"notfound") == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(L"big") == S_OK); - Assert::IsTrue(renameRegEx->Replace(L"foobar", &result) == S_OK); - Assert::IsTrue(wcscmp(result, L"foobar") == 0); - CoTaskMemFree(result); -} - -TEST_METHOD(ReplaceNoSearchOrReplaceTerm) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->Replace(L"foobar", &result) == S_OK); - Assert::IsTrue(result == nullptr); - CoTaskMemFree(result); -} - -TEST_METHOD(ReplaceNoReplaceTerm) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(L"foo") == S_OK); - Assert::IsTrue(renameRegEx->Replace(L"foobar", &result) == S_OK); - Assert::IsTrue(wcscmp(result, L"bar") == 0); - CoTaskMemFree(result); -} - -TEST_METHOD(ReplaceEmptyStringReplaceTerm) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(L"foo") == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(L"") == S_OK); - Assert::IsTrue(renameRegEx->Replace(L"foobar", &result) == S_OK); - Assert::IsTrue(wcscmp(result, L"bar") == 0); - CoTaskMemFree(result); -} - -TEST_METHOD(VerifyDefaultFlags) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = 0; - Assert::IsTrue(renameRegEx->GetFlags(&flags) == S_OK); - Assert::IsTrue(flags == 0); -} - -TEST_METHOD(VerifyCaseSensitiveSearch) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = CaseSensitive; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - SearchReplaceExpected sreTable[] = { - { L"Foo", L"Foo", L"FooBar", L"FooBar" }, - { L"Foo", L"boo", L"FooBar", L"booBar" }, - { L"Foo", L"boo", L"foobar", L"foobar" }, - { L"123", L"654", L"123456", L"654456" }, - }; - - for (int i = 0; i < ARRAYSIZE(sreTable); i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); - CoTaskMemFree(result); - } -} - -TEST_METHOD(VerifyReplaceFirstOnly) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = 0; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - SearchReplaceExpected sreTable[] = { - { L"B", L"BB", L"ABA", L"ABBA" }, - { L"B", L"A", L"ABBBA", L"AABBA" }, - { L"B", L"BBB", L"ABABAB", L"ABBBABAB" }, - }; - - for (int i = 0; i < ARRAYSIZE(sreTable); i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); - CoTaskMemFree(result); - } -} - -TEST_METHOD(VerifyReplaceAll) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = MatchAllOccurrences; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - SearchReplaceExpected sreTable[] = { - { L"B", L"BB", L"ABA", L"ABBA" }, - { L"B", L"A", L"ABBBA", L"AAAAA" }, - { L"B", L"BBB", L"ABABAB", L"ABBBABBBABBB" }, - }; - - for (int i = 0; i < ARRAYSIZE(sreTable); i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); - CoTaskMemFree(result); - } -} - -TEST_METHOD(VerifyReplaceAllCaseInsensitive) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = MatchAllOccurrences | CaseSensitive; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - SearchReplaceExpected sreTable[] = { - { L"B", L"BB", L"ABA", L"ABBA" }, - { L"B", L"A", L"ABBBA", L"AAAAA" }, - { L"B", L"BBB", L"ABABAB", L"ABBBABBBABBB" }, - { L"b", L"BBB", L"AbABAb", L"ABBBABABBB" }, - }; - - for (int i = 0; i < ARRAYSIZE(sreTable); i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); - CoTaskMemFree(result); - } -} - -TEST_METHOD(VerifyReplaceFirstOnlyUseRegEx) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = UseRegularExpressions; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - SearchReplaceExpected sreTable[] = { - { L"B", L"BB", L"ABA", L"ABBA" }, - { L"B", L"A", L"ABBBA", L"AABBA" }, - { L"B", L"BBB", L"ABABAB", L"ABBBABAB" }, - }; - - for (int i = 0; i < ARRAYSIZE(sreTable); i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); - CoTaskMemFree(result); - } -} - -TEST_METHOD(VerifyReplaceAllUseRegEx) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = MatchAllOccurrences | UseRegularExpressions; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - SearchReplaceExpected sreTable[] = { - { L"B", L"BB", L"ABA", L"ABBA" }, - { L"B", L"A", L"ABBBA", L"AAAAA" }, - { L"B", L"BBB", L"ABABAB", L"ABBBABBBABBB" }, - }; - - for (int i = 0; i < ARRAYSIZE(sreTable); i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); - CoTaskMemFree(result); - } -} - -TEST_METHOD(VerifyReplaceAllUseRegExCaseSensitive) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - DWORD flags = MatchAllOccurrences | UseRegularExpressions | CaseSensitive; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - SearchReplaceExpected sreTable[] = { - { L"B", L"BB", L"ABA", L"ABBA" }, - { L"B", L"A", L"ABBBA", L"AAAAA" }, - { L"b", L"BBB", L"AbABAb", L"ABBBABABBB" }, - }; - - for (int i = 0; i < ARRAYSIZE(sreTable); i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); - CoTaskMemFree(result); - } -} +#define TESTS_PARTIAL +#include "CommonRegExTests.h" TEST_METHOD(VerifyMatchAllWildcardUseRegEx) { @@ -275,38 +34,13 @@ TEST_METHOD(VerifyMatchAllWildcardUseRegEx) PWSTR result = nullptr; Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); CoTaskMemFree(result); } } -void VerifyReplaceFirstWildcard(SearchReplaceExpected sreTable[], int tableSize, DWORD flags) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - - for (int i = 0; i < tableSize; i++) - { - PWSTR result = nullptr; - Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); - Assert::AreEqual(sreTable[i].expected, result); - CoTaskMemFree(result); - } -} - -TEST_METHOD(VerifyReplaceFirstWildCardUseRegex) -{ - SearchReplaceExpected sreTable[] = { - //search, replace, test, result - { L".*", L"Foo", L"AAAAAA", L"Foo" }, - }; - VerifyReplaceFirstWildcard(sreTable, ARRAYSIZE(sreTable), UseRegularExpressions); -} - TEST_METHOD(VerifyReplaceFirstWildCardUseRegexMatchAllOccurrences) { SearchReplaceExpected sreTable[] = { @@ -316,27 +50,6 @@ TEST_METHOD(VerifyReplaceFirstWildCardUseRegexMatchAllOccurrences) VerifyReplaceFirstWildcard(sreTable, ARRAYSIZE(sreTable), UseRegularExpressions | MatchAllOccurrences); } -TEST_METHOD(VerifyReplaceFirstWildCardMatchAllOccurrences) -{ - SearchReplaceExpected sreTable[] = { - //search, replace, test, result - { L".*", L"Foo", L"AAAAAA", L"AAAAAA" }, - { L".*", L"Foo", L".*", L"Foo" }, - { L".*", L"Foo", L".*Bar.*", L"FooBarFoo" }, - }; - VerifyReplaceFirstWildcard(sreTable, ARRAYSIZE(sreTable), MatchAllOccurrences); -} - -TEST_METHOD(VerifyReplaceFirstWildNoFlags) -{ - SearchReplaceExpected sreTable[] = { - //search, replace, test, result - { L".*", L"Foo", L"AAAAAA", L"AAAAAA" }, - { L".*", L"Foo", L".*", L"Foo" }, - }; - VerifyReplaceFirstWildcard(sreTable, ARRAYSIZE(sreTable), 0); -} - TEST_METHOD(VerifyHandleCapturingGroups) { CComPtr renameRegEx; @@ -362,7 +75,8 @@ TEST_METHOD(VerifyHandleCapturingGroups) PWSTR result = nullptr; Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); CoTaskMemFree(result); } @@ -387,7 +101,8 @@ TEST_METHOD (VerifyFileAttributesNoPadding) Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); Assert::IsTrue(renameRegEx->PutFileTime(fileTime) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); CoTaskMemFree(result); } @@ -411,7 +126,8 @@ TEST_METHOD (VerifyFileAttributesPadding) Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); Assert::IsTrue(renameRegEx->PutFileTime(fileTime) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); CoTaskMemFree(result); } @@ -459,7 +175,8 @@ TEST_METHOD (VerifyFileAttributesMonthandDayNames) Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); Assert::IsTrue(renameRegEx->PutFileTime(fileTime) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == S_OK); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == S_OK); Assert::IsTrue(wcscmp(result, sreTable[i].expected) == 0); CoTaskMemFree(result); } @@ -483,33 +200,12 @@ TEST_METHOD(VerifyLookbehindFails) PWSTR result = nullptr; Assert::IsTrue(renameRegEx->PutSearchTerm(sreTable[i].search) == S_OK); Assert::IsTrue(renameRegEx->PutReplaceTerm(sreTable[i].replace) == S_OK); - Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result) == E_FAIL); + unsigned long index = {}; + Assert::IsTrue(renameRegEx->Replace(sreTable[i].test, &result, index) == E_FAIL); Assert::AreEqual(sreTable[i].expected, result); CoTaskMemFree(result); } } -TEST_METHOD(VerifyEventsFire) -{ - CComPtr renameRegEx; - Assert::IsTrue(CPowerRenameRegEx::s_CreateInstance(&renameRegEx) == S_OK); - CMockPowerRenameRegExEvents* mockEvents = new CMockPowerRenameRegExEvents(); - CComPtr regExEvents; - Assert::IsTrue(mockEvents->QueryInterface(IID_PPV_ARGS(®ExEvents)) == S_OK); - DWORD cookie = 0; - Assert::IsTrue(renameRegEx->Advise(regExEvents, &cookie) == S_OK); - DWORD flags = MatchAllOccurrences | UseRegularExpressions | CaseSensitive; - Assert::IsTrue(renameRegEx->PutFlags(flags) == S_OK); - Assert::IsTrue(renameRegEx->PutSearchTerm(L"FOO") == S_OK); - Assert::IsTrue(renameRegEx->PutReplaceTerm(L"BAR") == S_OK); - Assert::IsTrue(renameRegEx->PutFileTime(SYSTEMTIME{ 0 }) == S_OK); - Assert::IsTrue(renameRegEx->ResetFileTime() == S_OK); - Assert::IsTrue(lstrcmpi(L"FOO", mockEvents->m_searchTerm) == 0); - Assert::IsTrue(lstrcmpi(L"BAR", mockEvents->m_replaceTerm) == 0); - Assert::IsTrue(flags == mockEvents->m_flags); - Assert::IsTrue(renameRegEx->UnAdvise(cookie) == S_OK); - mockEvents->Release(); -} -} -; +}; } diff --git a/src/modules/powerrename/unittests/pch.h b/src/modules/powerrename/unittests/pch.h index 95c31bcf3f..3ca7e712fb 100644 --- a/src/modules/powerrename/unittests/pch.h +++ b/src/modules/powerrename/unittests/pch.h @@ -3,11 +3,4 @@ #include "targetver.h" #include - -// Headers for CppUnitTest - -// Suppressing 26466 - Don't use static_cast downcasts - in CppUnitTest.h -#pragma warning(push) -#pragma warning(disable : 26466) -#include "CppUnitTest.h" -#pragma warning(pop) +#include "CppUnitTestInclude.h" \ No newline at end of file