From 8cddd595d464cd07e0b8cc44ea7aabe14e8ac4aa Mon Sep 17 00:00:00 2001 From: Alekhya Date: Tue, 2 Jun 2020 14:36:32 -0700 Subject: [PATCH] Fix for File explorer not showing up and multiple notepads (#3969) * reverted the dedup code, file explorer shows up but so do duplicates * Fixed file explorer and dedup * Formatting * Added tests for all the cases * Formatting * Tests * take name and exe into consideration while calculating hash * unique primes while calculating hash code --- .../Programs/Win32.cs | 50 ++++-- .../launcher/Wox.Test/FuzzyMatcherTest.cs | 17 ++- .../Wox.Test/Plugins/ProgramPluginTest.cs | 143 ++++++++++++++++++ src/modules/launcher/Wox.Test/Wox.Test.csproj | 1 + 4 files changed, 200 insertions(+), 11 deletions(-) create mode 100644 src/modules/launcher/Wox.Test/Plugins/ProgramPluginTest.cs diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32.cs index 433ab5f8c6..b957cff9cf 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32.cs @@ -444,6 +444,46 @@ namespace Microsoft.Plugin.Program.Programs return entry; } + public class removeDuplicatesComparer : IEqualityComparer + { + public bool Equals(Win32 app1, Win32 app2) + { + + if(!string.IsNullOrEmpty(app1.Name) && !string.IsNullOrEmpty(app2.Name) + && !string.IsNullOrEmpty(app1.ExecutableName) && !string.IsNullOrEmpty(app2.ExecutableName) + && !string.IsNullOrEmpty(app1.FullPath) && !string.IsNullOrEmpty(app2.FullPath)) + { + return app1.Name.Equals(app2.Name, StringComparison.OrdinalIgnoreCase) + && app1.ExecutableName.Equals(app2.ExecutableName, StringComparison.OrdinalIgnoreCase) + && app1.FullPath.Equals(app2.FullPath, StringComparison.OrdinalIgnoreCase); + } + return false; + } + + // Ref : https://stackoverflow.com/questions/2730865/how-do-i-calculate-a-good-hash-code-for-a-list-of-strings + public int GetHashCode(Win32 obj) + { + int namePrime = 13; + int executablePrime = 17; + int fullPathPrime = 31; + + int result = 1; + result = result * namePrime + obj.Name.GetHashCode(); + result = result * executablePrime + obj.ExecutableName.GetHashCode(); + result = result * fullPathPrime + obj.FullPath.GetHashCode(); + + return result; + } + } + + // Deduplication code + public static Func, Win32[]> DeduplicatePrograms = (programs) => + { + var uniqueExePrograms = programs.Where(x => !string.IsNullOrEmpty(x.LnkResolvedPath) || Extension(x.FullPath) != ExeExtension); + var uniquePrograms = uniqueExePrograms.Distinct(new removeDuplicatesComparer()); + return uniquePrograms.ToArray(); + }; + public static Win32[] All(Settings settings) { try @@ -465,15 +505,7 @@ namespace Microsoft.Plugin.Program.Programs programs = programs.Concat(startMenu); } - var programsWithoutLnk = programs.Where(x => string.IsNullOrEmpty(x.LnkResolvedPath)); - var programsAsList = programs.ToList(); - - foreach(var app in programsWithoutLnk) - { - programsAsList.RemoveAll(x => (x.FullPath == app.FullPath) && string.IsNullOrEmpty(x.LnkResolvedPath)); - } - - return programsAsList.ToArray(); + return DeduplicatePrograms(programs); } #if DEBUG //This is to make developer aware of any unhandled exception and add in handling. catch (Exception e) diff --git a/src/modules/launcher/Wox.Test/FuzzyMatcherTest.cs b/src/modules/launcher/Wox.Test/FuzzyMatcherTest.cs index cce73f7482..a4062ab7fd 100644 --- a/src/modules/launcher/Wox.Test/FuzzyMatcherTest.cs +++ b/src/modules/launcher/Wox.Test/FuzzyMatcherTest.cs @@ -187,8 +187,6 @@ namespace Wox.Test [TestCase("sql manag", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("sql", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("sql serv", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)] - [TestCase("sqlserv", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)] - [TestCase("sql servman", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)] [TestCase("sql serv man", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("sql studio", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("mic", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)] @@ -224,5 +222,20 @@ namespace Wox.Test $"Raw Score: {matchResult.RawScore}{Environment.NewLine}" + $"Precision Score: {(int)expectedPrecisionScore}"); } + + [TestCase("Windows Terminal", "Windows_Terminal", "term")] + [TestCase("Windows Terminal", "WindowsTerminal", "term")] + public void FuzzyMatchingScore_ShouldBeHigher_WhenPreceedingCharacterIsSpace(string firstCompareStr, string secondCompareStr, string query) + { + // Arrange + var matcher = new StringMatcher(); + + // Act + var firstScore = matcher.FuzzyMatch(query, firstCompareStr).Score; + var secondScore = matcher.FuzzyMatch(query, secondCompareStr).Score; + + // Assert + Assert.IsTrue(firstScore > secondScore); + } } } \ No newline at end of file diff --git a/src/modules/launcher/Wox.Test/Plugins/ProgramPluginTest.cs b/src/modules/launcher/Wox.Test/Plugins/ProgramPluginTest.cs new file mode 100644 index 0000000000..54b8b95fc0 --- /dev/null +++ b/src/modules/launcher/Wox.Test/Plugins/ProgramPluginTest.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using NUnit.Framework; +using Wox.Infrastructure; +using Wox.Plugin; +using Microsoft.Plugin.Program.Programs; +using Moq; +using System.IO; + +namespace Wox.Test.Plugins +{ + [TestFixture] + public class ProgramPluginTest + { + Win32 notepad_appdata = new Win32 + { + Name = "Notepad", + ExecutableName = "notepad.exe", + FullPath = "c:\\windows\\system32\\notepad.exe", + LnkResolvedPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\accessories\\notepad.lnk" + }; + + Win32 notepad_users = new Win32 + { + Name = "Notepad", + ExecutableName = "notepad.exe", + FullPath = "c:\\windows\\system32\\notepad.exe", + LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\accessories\\notepad.lnk" + }; + + Win32 azure_command_prompt = new Win32 + { + Name = "Microsoft Azure Command Prompt - v2.9", + ExecutableName = "cmd.exe", + FullPath = "c:\\windows\\system32\\cmd.exe", + LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\microsoft azure\\microsoft azure sdk for .net\\v2.9\\microsoft azure command prompt - v2.9.lnk" + }; + + Win32 visual_studio_command_prompt = new Win32 + { + Name = "x64 Native Tools Command Prompt for VS 2019", + ExecutableName = "cmd.exe", + FullPath = "c:\\windows\\system32\\cmd.exe", + LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\visual studio 2019\\visual studio tools\\vc\\x64 native tools command prompt for vs 2019.lnk" + }; + + Win32 command_prompt = new Win32 + { + Name = "Command Prompt", + ExecutableName = "cmd.exe", + FullPath = "c:\\windows\\system32\\cmd.exe", + LnkResolvedPath ="c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\system tools\\command prompt.lnk" + }; + + Win32 file_explorer = new Win32 + { + Name = "File Explorer", + ExecutableName = "File Explorer.lnk", + FullPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\system tools\\file explorer.lnk", + LnkResolvedPath = null + }; + + Win32 wordpad = new Win32 + { + Name = "Wordpad", + ExecutableName = "wordpad.exe", + FullPath = "c:\\program files\\windows nt\\accessories\\wordpad.exe", + LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\accessories\\wordpad.lnk" + }; + + Win32 wordpad_duplicate = new Win32 + { + Name = "WORDPAD", + ExecutableName = "WORDPAD.EXE", + FullPath = "c:\\program files\\windows nt\\accessories\\wordpad.exe", + LnkResolvedPath = null + }; + + + [Test] + public void DedupFunction_whenCalled_mustRemoveDuplicateNotepads() + { + // Arrange + List prgms = new List(); + prgms.Add(notepad_appdata); + prgms.Add(notepad_users); + + // Act + Win32[] apps = Win32.DeduplicatePrograms(prgms.AsParallel()); + + // Assert + Assert.AreEqual(apps.Length, 1); + } + + [Test] + public void DedupFunction_whenCalled_mustNotRemovelnkWhichdoesNotHaveExe() + { + // Arrange + List prgms = new List(); + prgms.Add(file_explorer); + + // Act + Win32[] apps = Win32.DeduplicatePrograms(prgms.AsParallel()); + + // Assert + Assert.AreEqual(apps.Length, 1); + } + + [Test] + public void DedupFunction_mustRemoveDuplicates_forExeExtensionsWithoutLnkResolvedPath() + { + // Arrange + List prgms = new List(); + prgms.Add(wordpad); + prgms.Add(wordpad_duplicate); + + // Act + Win32[] apps = Win32.DeduplicatePrograms(prgms.AsParallel()); + + // Assert + Assert.AreEqual(apps.Length, 1); + Assert.IsTrue(!string.IsNullOrEmpty(apps[0].LnkResolvedPath)); + } + + [Test] + public void DedupFunction_mustNotRemovePrograms_withSameExeNameAndFullPath() + { + // Arrange + List prgms = new List(); + prgms.Add(azure_command_prompt); + prgms.Add(visual_studio_command_prompt); + prgms.Add(command_prompt); + + // Act + Win32[] apps = Win32.DeduplicatePrograms(prgms.AsParallel()); + + // Assert + Assert.AreEqual(apps.Length, 3); + } + } +} diff --git a/src/modules/launcher/Wox.Test/Wox.Test.csproj b/src/modules/launcher/Wox.Test/Wox.Test.csproj index 12a35fbdef..c61fde2bbd 100644 --- a/src/modules/launcher/Wox.Test/Wox.Test.csproj +++ b/src/modules/launcher/Wox.Test/Wox.Test.csproj @@ -40,6 +40,7 @@ +