diff --git a/src/modules/powerrename/lib/Helpers.cpp b/src/modules/powerrename/lib/Helpers.cpp index 73991c8e9b..040167d6ef 100644 --- a/src/modules/powerrename/lib/Helpers.cpp +++ b/src/modules/powerrename/lib/Helpers.cpp @@ -46,6 +46,11 @@ HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR sour { std::locale::global(std::locale("")); HRESULT hr = E_INVALIDARG; + + auto contractionOrSingleQuotedWordCheck = [](std::wstring stem, size_t i) { + return !i || stem[i - 1] != '\'' || (i == 1 || iswpunct(stem[i - 2]) || iswspace(stem[i - 2])); + }; + if (source && flags) { if (flags & Uppercase) @@ -156,7 +161,7 @@ HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR sour for (size_t i = 0; i < stemLength; i++) { - if (!i || iswspace(stem[i - 1]) || iswpunct(stem[i - 1])) + if (!i || iswspace(stem[i - 1]) || (iswpunct(stem[i - 1]) && contractionOrSingleQuotedWordCheck(stem, i))) { if (iswspace(stem[i]) || iswpunct(stem[i])) { @@ -167,7 +172,10 @@ HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR sour { wordLength++; } - if (isFirstWord || i + wordLength == stemLength || std::find(exceptions.begin(), exceptions.end(), stem.substr(i, wordLength)) == exceptions.end()) + + auto subStr = stem.substr(i, wordLength); + std::transform(subStr.begin(), subStr.end(), subStr.begin(), ::towlower); + if (isFirstWord || i + wordLength == stemLength || std::find(exceptions.begin(), exceptions.end(), subStr) == exceptions.end()) { stem[i] = towupper(stem[i]); isFirstWord = false; @@ -205,13 +213,16 @@ HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR sour for (size_t i = 0; i < stemLength; i++) { - if (!i || iswspace(stem[i - 1]) || iswpunct(stem[i - 1])) + if (!i || iswspace(stem[i - 1]) || (iswpunct(stem[i - 1]) && contractionOrSingleQuotedWordCheck(stem, i))) { if (iswspace(stem[i]) || iswpunct(stem[i])) { continue; } - stem[i] = towupper(stem[i]); + else + { + stem[i] = towupper(stem[i]); + } } else { diff --git a/src/modules/powerrename/unittests/PowerRenameManagerTests.cpp b/src/modules/powerrename/unittests/PowerRenameManagerTests.cpp index c3a57dfc8b..c6145ff0a7 100644 --- a/src/modules/powerrename/unittests/PowerRenameManagerTests.cpp +++ b/src/modules/powerrename/unittests/PowerRenameManagerTests.cpp @@ -91,11 +91,15 @@ namespace PowerRenameManagerTests Assert::IsTrue(replaceSuccess); + std::vector shouldRename = { L"not ", L"" }; + // Verify the rename occurred for (int i = 0; i < numPairs; i++) { - Assert::IsTrue(testFileHelper.PathExists(renamePairs[i].originalName) == !renamePairs[i].shouldRename); - Assert::IsTrue(testFileHelper.PathExists(renamePairs[i].newName) == renamePairs[i].shouldRename); + Assert::IsTrue(testFileHelper.PathExistsCaseSensitive(renamePairs[i].originalName) == !renamePairs[i].shouldRename, + (std::wstring(L"The path: [" + renamePairs[i].originalName + L"] should ") + shouldRename[!renamePairs[i].shouldRename] + L"exist.").c_str()); + Assert::IsTrue(testFileHelper.PathExistsCaseSensitive(renamePairs[i].newName) == renamePairs[i].shouldRename, + (std::wstring(L"The path: [" + renamePairs[i].newName + L"] should ") + shouldRename[renamePairs[i].shouldRename] + L"exist.").c_str()); } Assert::IsTrue(mgr->Shutdown() == S_OK); @@ -250,7 +254,19 @@ namespace PowerRenameManagerTests TEST_METHOD (VerifyTitlecaseTransform) { rename_pairs renamePairs[] = { - { L"foo and the to", L"Bar and the To", false, true, 0 }, + { L"foo And The To", L"Bar and the To", false, true, 0 }, + { L"foo And The To.txt", L"Bar and the To.txt", true, true, 0 }, + { L"Test", L"Test_norename", false, false, 0 } + }; + + RenameHelper(renamePairs, ARRAYSIZE(renamePairs), L"foo", L"bar", SYSTEMTIME{ 2020, 7, 3, 22, 15, 6, 42, 453 }, DEFAULT_FLAGS | Titlecase); + } + + TEST_METHOD (VerifyTitlecaseWithApostropheTransform) + { + rename_pairs renamePairs[] = { + { L"the foo i'll and i've you're dogs' the i'd it's i'm don't to y'all", L"The Bar I'll and I've You're Dogs' the I'd It's I'm Don't to Y'all", false, true, 0 }, + { L"'the 'foo' 'i'll' and i've you're dogs' the 'i'd' it's i'm don't to y'all.txt", L"'The 'Bar' 'I'll' and I've You're Dogs' the 'I'd' It's I'm Don't to Y'all.txt", true, true, 0 }, { L"Test", L"Test_norename", false, false, 0 } }; @@ -267,10 +283,22 @@ namespace PowerRenameManagerTests RenameHelper(renamePairs, ARRAYSIZE(renamePairs), L"foo", L"bar", SYSTEMTIME{ 2020, 7, 3, 22, 15, 6, 42, 453 }, DEFAULT_FLAGS | Capitalized); } + TEST_METHOD (VerifyCapitalizedWithApostropheTransform) + { + rename_pairs renamePairs[] = { + { L"foo i'll and i've you're dogs' the i'd it's i'm don't to y'all", L"Bar I'll And I've You're Dogs' The I'd It's I'm Don't To Y'all", false, true, 0 }, + { L"'foo i'll 'and' i've you're dogs' the i'd it's i'm don't to y'all.txt", L"'Bar I'll 'And' I've You're Dogs' The I'd It's I'm Don't To Y'all.txt", true, true, 0 }, + { L"Test", L"Test_norename", false, false, 0 } + }; + + RenameHelper(renamePairs, ARRAYSIZE(renamePairs), L"foo", L"bar", SYSTEMTIME{ 2020, 7, 3, 22, 15, 6, 42, 453 }, DEFAULT_FLAGS | Capitalized); + } + TEST_METHOD (VerifyNameOnlyTransform) { rename_pairs renamePairs[] = { - { L"foo.txt", L"BAR.txt", false, true, 0 }, + { L"foo.foo", L"BAR.foo", true, true, 0 }, + { L"foo.txt", L"BAR.TXT", false, true, 0 }, { L"TEST", L"TEST_norename", false, false, 1 } }; diff --git a/src/modules/powerrename/unittests/TestFileHelper.cpp b/src/modules/powerrename/unittests/TestFileHelper.cpp index 6d55ae0659..e7b2a1a3ea 100644 --- a/src/modules/powerrename/unittests/TestFileHelper.cpp +++ b/src/modules/powerrename/unittests/TestFileHelper.cpp @@ -47,6 +47,19 @@ bool CTestFileHelper::PathExists(_In_ const std::wstring path) return fs::exists(fullPath); } +bool CTestFileHelper::PathExistsCaseSensitive(_In_ const std::wstring path) +{ + fs::path tempDirPath = fs::path(_tempDirectory); + for (const auto& entry : fs::directory_iterator(tempDirPath)) + { + if (entry.path().filename().wstring() == path) + { + return true; + } + } + return false; +} + bool CTestFileHelper::_CreateTempDirectory() { // Initialize to the temp directory diff --git a/src/modules/powerrename/unittests/TestFileHelper.h b/src/modules/powerrename/unittests/TestFileHelper.h index 0ccfe1f641..8f6f12c96b 100644 --- a/src/modules/powerrename/unittests/TestFileHelper.h +++ b/src/modules/powerrename/unittests/TestFileHelper.h @@ -14,6 +14,7 @@ public: bool AddFolder(_In_ const std::wstring path); const std::filesystem::path GetTempDirectory() { return _tempDirectory; } bool PathExists(_In_ const std::wstring path); + bool PathExistsCaseSensitive(_In_ const std::wstring path); std::filesystem::path GetFullPath(_In_ const std::wstring path); private: